XLSForm / pyxform

A Python package to create XForms for ODK Collect.
BSD 2-Clause "Simplified" License
75 stars 133 forks source link

Custom namespace in custom attribute column results in a serialized object in the xml attribute #696

Closed jkuester closed 4 months ago

jkuester commented 4 months ago

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': &quot;Hello's World!&quot;, 'message2': 'Uses's &quot;both&quot; 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"/>

lognaturel commented 4 months ago

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.

lindsay-stevens commented 4 months ago

@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>
lognaturel commented 4 months ago

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!

jkuester commented 4 months ago

Oh wow! Thanks @lindsay-stevens for catching that! :facepalm: 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! :heart: