Converting XML Form to XLSForm survey

Hello all! Hope you're having a great day!

I am trying to set up a completely offline team. Part of what i need to be able to do is for them to use existing xml surveys to produce XLSForm surveys (with survey, choices, and settings sheets, normal columns (label, name, type, appearance, relevance, etc.)) so that they can edit the XLSForm before using ODKOffline to produce the updated XML file.

My team is not Techy enough to manipulate XML surveys directly.

Any tool i can use to do that?

Would installing ODK aggregate offline on a VM allow me to then "download" an XLSForm? I have not yet experimented with this option. (Add on question, is the VM/Offline Aggregate user friendly?)

Best regards,
You're friendly neighborhood MEAL Manager

Converting an arbitrary XML encoded XForm to XSLForm is tricky and AFAIK there are no automated tools to accomplish this - at least not in the general case. One of the issues is that with an XLSForm instance element names have to be globally unique (ie flat namespace over entire form), whereas in an arbitrary XML XForm you can have the same element name appearing under different groups (in XML its the full group hierarchy path name that uniquely identifies each instance element). This means you cannot readily convert an XML element name, say /data/group1/subgroup2/name, to an XLSForm ${name} reference, because your XML form could easily also contain /data/group2/subgroup3/name for an entirely different question!

FYI Aggregate may allow you to load an XML form but doesn't allow you to export an XLSForm. ODK Build does allow you to export an XLSForm (or XML) but you can only import forms in its custom JSON format. Other ODK compatible tools like KoboToolbox will import and export XLSForms, export XML, but again cannot import XML.

So you may well have to tweak the XML, or (manually?) translate the XML form to an equivalent XLSForm and start using that. How big/many are these forms? Is this something you could contract out? There are folks on the forum good at writing XLSForms that might be able to assist.

Although the inverse is technically not impossible, XLSForm to XML form is a bit of a one-way street...:neutral_face:

1 Like

Thanks for your rapid and thorough response!

The workload is not heavy enough to contract it, while at the same time not light enough for us to do it.

An alternative solution would be for us to send the form on a USB key as a start.

The situation is i have two teams, 4 hours apart, one online, one offline and they do not have easy access to each other. The online team is the one that typically creates the surveys while the offline team is the one that does the data collection. We can occasionally visit the offline team to give them android phones with the surveys on them. But due to limited access and distance this is not always feasible. I was trying to create an independent team that would still primarily receive the surveys from the online team but would be able to manage them independently if needed.

Solution: When giving phones with survey provide them with XLSForm copy as a Standard Operating Procedure. They can manipulate that and use ODKOffline and ODKvalidate to produce a working XML :wink:

Thanks once more for your answer!

"its custom JSON format.", Add on question: Can i convert XLSForm to the files ODKbuild can read?

Best!

(I think you mean "convert XML to...")

"technically not impossible", but I'll let you be the judge how 'light' it'll be... :wink:

Here's a simple 3 question form: 1 text question, 1 number, 1 date.

In ODK Build's native JSON format:

{"title":"My First Form","controls":[{"name":"text","label":{"0":"This is a text question"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","length":false,"metadata":{},"type":"inputText"},{"name":"number","label":{"0":"This is a number quesion"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"appearance":"Textbox","kind":"Integer","selectRange":{"min":"1","max":"10"},"selectStep":"1","sliderTicks":true,"metadata":{},"type":"inputNumeric"},{"name":"date","label":{"0":"This is a date question"},"hint":{},"defaultValue":"","readOnly":false,"required":false,"requiredText":{},"relevance":"","constraint":"","invalidText":{},"calculate":"","range":false,"kind":"Full Date","metadata":{},"type":"inputDate"}],"metadata":{"version":2,"activeLanguages":{"0":"English","_counter":0,"_display":"0"},"optionsPresets":[],"htitle":null,"instance_name":"","public_key":"","submission_url":""}}

In XForm XML:

<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa">
  <h:head>
    <h:title>My First Form</h:title>
    <model>
      <instance>
        <data id="build_My-First-Form_1550731957">
          <meta>
            <instanceID/>
          </meta>
          <text/>
          <number/>
          <date/>
        </data>
      </instance>
      <itext>
        <translation lang="English">
          <text id="/data/text:label">
            <value>This is a text question</value>
          </text>
          <text id="/data/number:label">
            <value>This is a number quesion</value>
          </text>
          <text id="/data/date:label">
            <value>This is a date question</value>
          </text>
        </translation>
      </itext>
      <bind nodeset="/data/meta/instanceID" type="string" readonly="true()" calculate="concat('uuid:', uuid())"/>
      <bind nodeset="/data/text" type="string"/>
      <bind nodeset="/data/number" type="int"/>
      <bind nodeset="/data/date" type="date"/>
    </model>
  </h:head>
  <h:body>
    <input ref="/data/text">
      <label ref="jr:itext('/data/text:label')"/>
    </input>
    <input ref="/data/number">
      <label ref="jr:itext('/data/number:label')"/>
    </input>
    <input ref="/data/date">
      <label ref="jr:itext('/data/date:label')"/>
    </input>
  </h:body>
</h:html>

And finally an XSLForm:

My-First-Form-export.xlsx (16.5 KB)

As you can see, the XML format is arguably the most complex and difficult to interpret, ie parse, which is precisely what you'll have to do to translate it into something more consumable (aka XLSForm). The thing with XML XForms is that each question is effectively broken apart and put in three different sections of the XML format: the instance data (<data>...</data>), binding section (<bind/><bind/>...) and finally the control section (<input>...</input>). Whereas formats like XLSForm and ODK Build's JSON ostensibly collect all the info about each question together - one row per question in the case of XLSForm, or one JSON dictionary per question in the case of ODK Build.

So translating an XML form to either of these formats first requires extracting all the necessary data for each question from very different places in the XML form, which in practice requires a pretty high level of technical expertise (eg XSLT and XPath experience). And even that is completely ignoring things like repeat groups and translations!

Unfortunately, if these (XML) forms are business-critical, and you need to regularly edit them, either you'll have to learn/train folks to edit the raw XML (yuck!), or bite-the-bullet and rewrite them as either XLSForms or ODK Build (depending on your preference for form builder).

3 Likes

Thanks for your support and dedicated response!

Unfortunately, manual conversion will not be possible as I do not have neither the technical capacity nor the human resources and it is not enough work to require contracting it. However, we are working on providing internet where my remotely managed team is + we will make it a habit to attach the XLSform with all phones we send to the field :slight_smile:

Thanks again!