Custom namespace in custom attribute column results in a serialized object in the xml attribute
jkuester opened this issue · 4 comments
Software and hardware versions
pyxform v2.0.2, Python 3.12.1
Problem description
If you have a custom attribute column in your spreadsheet without the namespace (e.g. instance::duration
) this gets converted to something like <my_timer duration="10"/>
in the form XML output from pyxform. However, with the custom namespace (as recommended in the spec, e.g. instance::cht::duration
), you get something like <my_timer cht="{'duration': '10'}"/>
.
This serialized dictionary is pretty tricky to parse with something like JavaScript especially when you start including quotes and stuff in your column values: <my_timer cht="{'duration': '5', 'message': "Hello's World!", 'message2': 'Uses's "both" kinds of quotes'}"/>
.
You can recreate this behavior by using pyxform to convert a spreadsheet with data like:
type | name | instance::cht::duration |
---|---|---|
trigger | my_trigger | 10 |
Expected behavior
I guess my first question is if what I have described here is actually the intended behavior? If so, is there any recommendation on how to parse the data from those attributes? (Maybe that is just the normal string serialization of a Python dict? Then it would be easy to parse from a Python context...)
The main alternative behavior I wanted to propose (and how I had initially expected things to work) was to just create individual xml attributes with the custom namespace. So, an instance::cht::duration
column in your spreadsheet would result in something like this in the form XML: <my_timer cht:duration="10"/>
That's most certainly not the intended behavior. You declared your namespace in the settings sheet? Looks like a bug and we'd welcome a PR for it.
@jkuester this is a typo issue, there's one too many colons. Try naming your custom column instance::cht:duration
instead of instance::cht::duration
.
The namespace delimiter is double colon ::
, pyxform uses this to break column names down into dictionaries. The property delimiter is single colon :
, it is treated as part of the property name and so goes straight in to the XML as a standard namespace prefix. The docs show the correct usage.
Example XLSForm:
def test_namespaced_property(self):
md = """
| survey | | | | |
| | type | name | label | instance::cht:duration |
| | trigger | my_trigger | T1 | 10 |
| settings | |
| | namespaces |
| | cht="http://cht.com/xforms" |
"""
self.assertPyxformXform(
md=md,
debug=True,
)
Result XForm:
<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa" xmlns:orx="http://openrosa.org/xforms" xmlns:odk="http://www.opendatakit.org/xforms" xmlns:cht="http://cht.com/xforms">
<h:head>
<h:title>test_title</h:title>
<model odk:xforms-version="1.0.0">
<instance>
<test_name id="test_id">
<my_trigger cht:duration="10"/>
<meta>
<instanceID/>
</meta>
</test_name>
</instance>
<bind nodeset="/test_name/meta/instanceID" type="string" readonly="true()" jr:preload="uid"/>
</model>
</h:head>
<h:body>
<trigger ref="/test_name/my_trigger">
<label>T1</label>
</trigger>
</h:body>
</h:html>
there's one too many colons
Oh, good catch, I totally missed that. I was pretty sure it was in use so it seemed very surprising it wouldn't work as expected. Glad you saw it!
Oh wow! Thanks @lindsay-stevens for catching that! 🤦 Never thought twice about double-checking my header format since I was getting something in the form xml...
Much gratitude for the quick and helpful support here! ❤️