The Idea
One of the ideas we've had whilst developing XForms over the years has been to try and generate a form from a set of XML instance data. The following article takes a look at one attempt at doing this.
The main idea was that for every leaf node (a node with no children) in a set of XML instance data, we wanted to create an XForms input control and for every non-leaf node we'd create an XForms group control. The result would be that we'd have a very simple XForm that contained controls bound to the instance data.
The generation of the simple XForm we decided would be done with XSLT.
The Example
As always, we start with some example instance data, in this case representing a dummy set of 'account' information (note that it's not a mixed namespace document, which keeps things a bit simpler):
<account account_id="XYZ0123456"> <client_details> <client client_id="C0000001"> <title>Mr</title> <first_name>Joe</first_name> <surname>Bloggs</surname> <date_of_birth>1950-07-15</date_of_birth> <gender>M</gender> <address> <line_1>10 High Street</line_1> <line_2>Averageville</line_2> <line_3>Madeupshire</line_3> <line_4>MU994RF</line_4> </address> <contact> <daytime_phone>07987654321</daytime_phone> <evening_phone>01234567890</evening_phone> <email>joe.bloggs@example.co.uk</email> </contact> </client> <client client_id="C0000002"> <title>Mrs</title> <first_name>Jane</first_name> <surname>Bloggs</surname> <date_of_birth>1952-09-01</date_of_birth> <gender>F</gender> <address> <line_1>10 High Street</line_1> <line_2>Averageville</line_2> <line_3>Madeupshire</line_3> <line_4>MU994RF</line_4> </address> <contact> <daytime_phone>07987123456</daytime_phone> <evening_phone>01234567890</evening_phone> <email>jane.bloggs@example.co.uk</email> </contact> </client> </client_details> <contact_preferences> <primary_account_contact_ref>C0000002</primary_account_contact_ref> <contact_via_phone>false</contact_via_phone> <contact_via_email>true</contact_via_email> </contact_preferences> </account>
The next part of the process is to start on writing the XSLT that will transform the data into an XForm.
We're outputting an (X)HTML document so the first xsl:template element we need matches the instance data's root element and inserts the html element (with some namespace declarations), the head and body tags into the output stream.
<xsl:template match="/"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <head> ... </head> <body> ... </body> ... </html> </xsl:template>
Also in this xsl:template we add the contents of the head element. I'm going to be using the Ubiquity XForms javascript library so the contents contain a script tag that links to that, plus the HTML title tag and some basic inline CSS. Obviously, if you are using a different XForms processor then you'll omit the script tag, and add whatever is needed for your processor:
... <head> <title><xsl:value-of select="$form_title"/></title> <script type="text/javascript" src="ubiquity-xforms-0.7.1/build/dist/src/ubiquity-loader.js">/**/</script> <style type="text/css"> xf\:group, xf\:input{ display: block; margin-bottom: 10px; } xf\:label{ width: 200px; } .repeated-group{ border: 1px solid #000; } </style> </head> ...
Notice that the contents of the title tag is an XSL param. There's a number of xsl:params defined above the first template which let us influence the output of the translation (they've all got default values). This one's just letting us set the document's title.
Still in the initial xsl:template I now need to insert the code that will generate the XForm model, instance and form controls. As I'm using the Ubiquity XForms library, the xf:model element and its children are required to be placed within the documents body tag.
... <body> <xsl:element name="xf:model"> <xsl:attribute name="id"><xsl:value-of select="$xf_model_id" /></xsl:attribute> <xsl:choose> <xsl:when test="$xf_instance_inline = 'true'"> <xf:instance id="{$xf_instance_id}"> <xsl:apply-templates mode="inst-data" /> </xf:instance> </xsl:when> <xsl:otherwise> <xf:instance id="{$xf_instance_id}" src="{$xf_instance_src}" /> </xsl:otherwise> </xsl:choose> </xsl:element> </body> ...
As you can see, we make use of a few more XSLT parameters. One sets the value of the xf:model/@id attribute, one is used to choose whether or not we want to include the instance data inline in the generated form or link the xf:instance to it in a separate file utilising the xf:instance/@src attribute. In the example I'm describing here, we'll have the instance data inline in the form.
When including the instance data inline we take advantage of the @mode attribute of xsl:apply-templates which lets us process a node (or set of nodes) more than once.
We need to process the instance data once to copy it into the transform output stream as children of the xf:instance element so that it can be used as XForm instance data, and again to generate the form controls (we could also process it again to generate a set of xf:bind elements too).
To copy through the data for use as XForms instance data requires a single xsl:template:
... <xsl:template match="@* | node()" mode="inst-data"> <xsl:copy> <xsl:apply-templates select="@* | node()" mode="inst-data" /> </xsl:copy> </xsl:template> ...
We've now inserted all the code that should now generate the XForms model element and it's children. We need to add code that generates the XForms controls which is the semi-tricky bit. To do this we process the instance data again using a different @mode (the context node of the xsl:template we're in is still '/'):
... <xsl:apply-templates mode="xf-controls"> <xsl:with-param name="xpath_prefix">/</xsl:with-param> </xsl:apply-templates> ...
We now need to add a number of xsl:templates that match nodes in different situations in the input document so that the right XForms control is output. The rules we're using are that non-leaf nodes are output as xf:group controls and leaf nodes are output as xf:input controls.
The simplest new xsl:template we need is for attribute nodes. They are all leaf nodes and always translated to xf:inputs:
... <xsl:template match="@*" mode="xf-controls"> <xf:input ref="@{name(.)}"> <xf:label>(@<xsl:value-of select="name(.)"/>)</xf:label> </xf:input> </xsl:template> ...
Now we need to match and process remaining nodes from the input document. The problem here is that for each of the nodes matched we need to work out if they are a leaf node or not. This is done with further xsl:templates, using the @match attribute and an XPath expression to differentiate leaf from non-leaf by seeing if the node currently being processed has any descendants or attributes.
Firstly an xsl:template for non-leaf nodes:
... <xsl:template match="*[not(contains(name(.), ':')) and (count(descendant::*) > 0 or count(attribute::*) > 0)]" mode="xf-controls"> <xsl:param name="xpath_prefix"></xsl:param> <xsl:variable name="current_node_name" select="name(.)" /> <xsl:variable name="current_node_position" select="count(preceding-sibling::*[name() = $current_node_name]) + 1" /> <xsl:call-template name="non-leaf-node"> ... </xsl:call-template> </xsl:template> ...
Then leaf nodes:
... <xsl:template match="*[not(contains(name(.), ':')) and (count(descendant::*) = 0 and count(attribute::*) = 0)]" mode="xf-controls"> <xsl:param name="xpath_prefix"></xsl:param> <xsl:variable name="current_node_name" select="name(.)" /> <xsl:variable name="current_node_position" select="count(preceding-sibling::*[name() = $current_node_name]) + 1" /> <xsl:call-template name="non-leaf-node"> ... </xsl:call-template> </xsl:template> ...
Each of the xsl:templates sets a couple of variable values that works out where the current node is in the document (if its got siblings of the same name, etc.) and calls a named xsl:template with these values. The called templates are then responsible for outputting the correct XForm control that references the correct piece of XForms instance data.
Here's the named template that decides what's output for a non-leaf node (the leaf template is similar but outputs an input control instead):
<xsl:template name="non-leaf-node"> <xsl:param name="ref_value" /> <xsl:param name="has_siblings_of_same_name" /> <xsl:param name="position" /> <xsl:param name="label_value" /> <xsl:choose> <xsl:when test="$has_siblings_of_same_name"> <xf:group ref="{$ref_value}[{$position}]" class="repeated-group"> <xsl:if test="$xf_group_labels = 'true'"> <xf:label>(<xsl:value-of select="$label_value"/>)</xf:label> </xsl:if> <xsl:apply-templates select="@* | node()" mode="xf-controls" /> </xf:group> </xsl:when> <xsl:otherwise> <xf:group ref="{$ref_value}"> <xsl:if test="$xf_group_labels = 'true'"> <xf:label>(<xsl:value-of select="$label_value"/>)</xf:label> </xsl:if> <xsl:apply-templates select="@* | node()" mode="xf-controls" /> </xf:group> </xsl:otherwise> </xsl:choose> </xsl:template>
One of the important things in the above template is the value of the xf:group/@ref attribute. If there's more than one node with the same name at any level of the input instance data, the generated controls need to reference the different nodes using the node position in the document, otherwise all the controls will show the same information.
Also, by identifying controls that have siblings of the same name -- by adding @class="repeated-group" -- we could run further transforms on the output XForm and replace these controls with things like xf:repeat controls.
There's a zip file attached that contains the instance data, XSLT and a batch file that runs the transform. (You'll need the Saxon XSLT engine, and will need to edit the .bat file for use on your machine. There are more details in the readme file.)
If you don't want to download and the attachment and the transform yourself, the generated XForm can be seen here.
The Conclusion
Whilst this is by no means perfect, it shows that it is possible to take a piece of XML instance data and very quickly get to a working XForm from it. As I mentioned, further transforms on the generated form could achieve even more functionality.
| Attachment | Size |
|---|---|
| instance-to-xform.zip | 5.11 KB |

Great thing to share
Great thing to share man.Theses | Dissertations
Amazing post, but try to make
Amazing post, but try to make it further precise next time.
Essay | Assignments | Book Reports
I have been trying to figure
I have been trying to figure this out as well. I cannot establish if there is any relationship between them or not. I am going to do some more research and will let you know what I find out. Plastic Injection Molding
I also found your posts very
I also found your posts very interesting. In fact after reading, I had to go show it to my friend and he ejoyed it as well! Internet Hosting save fuel reverse phone detective tava tea error fix
its cool. Seo
its cool.
Seo expert
SEO
search marketing
I really loved reading your
I really loved reading your blog. It was very well authored and easy to undertand. Unlike additional blogs I have read which are really not tht good. I also found your posts very interesting. In fact after reading, I had to go show it to my friend and he ejoyed it as well! Wagoner Criminal Defense Attorney
The post is very nicely
The post is very nicely written and it contains many useful facts. I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement. Thanks for sharing with us. It's nice to see this type of post Forex broker
I found so many interesting
I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work. Denver Roofing
kamagra buy viagra
kamagra buy
viagra supplier
viagra wholesaler
viagra distributor
Viagra
Generic Viagra
Impotence
The post is very nicely
The post is very nicely written and it contains many useful facts. I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement. Thanks for sharing with us. It's nice to see this type of post logistics management
I enjoyed every bit of it and
I enjoyed every bit of it and I've marked to see new things for the good blog visit.I post.Really glad this submission! I've been looking for some extra help. Social Media Wichita
I have been working with
I have been working with XForms in OpenOffice and have been able to create and submit to a local external (to the OpenOffice document) XML data file. The problem is that both the Instance reference to the external XML file and the Submit reference to the external XML file seem to require an absolute path, i.e., file:///C:/Tempfile/MyInstance.xml. The XForms specification clearly says that a relative path can be used, i.e., MyInstance.xml. Without the relative path, the document is not portable, because unless it is saved on someone else's computer in exactly the same file location, the OpenOffice XForms document will not find the external XML file. Has anyone else tried this? Any comments or suggestions from the XForms programmers? Is the relative path just not implemented yet? This has to work with relative paths or it isn't suitable for use by average computer users who need to be able to read and fill out a form by saving it to any convenient location on their computer, then just opening it with OpenOffice.
Hello! Would like to help.
Hello!
Would like to help. Downloaded this information in a tutorial on XForms from pdf search engine http://www.pdfok.com I must admit we share the same disappointment with regards to absolute path requirement of the PUT function of XForms but XForms is designed to gather instance data, serialize it into an external representation and submit it with a protocol to a designated destination, normally it's a URL path and the server takes over, saving the data to disk on the client computer is just extra function I guess, if you can work around this limitation, let us know.
Cheers!
I guess I might want to
I guess I might want to download it instead,which saves me much trouble...
free advertising |job listings|tempurpedic
I guess I might want to
I guess I might want to download it instead,which saves me much trouble...
free advertising |job listings|tempurpedic
In my auto insurance quotes i
In my auto insurance quotes i run wordpress,will this work with wordpress?
Regards,Mike
Hello Alex! This was an
Hello Alex!
This was an excellent article. Keep up the great work.
Although instance documents are a great start, generating a selection list from a single instance is not possible. You can also easily convert instance documents to XML Schemas using XML IDEs such as oXygen.
An alternative approach is to generate an XForms documents directly and dynamically from an XML Schema. This allows you to regenerate the form whenever the XML Schema changes. This is called "model-drive" development since the XForms artifacts are generated directly from the model and can be kept in sync.
The problem with generating XForms from XML Schemas is that XML Schemas come with so many variations that it becomes a difficult task to process them in general.
An alternative is to use XML Schema standards like the US National Information Exchange Model (NIEM.gov).
Here is an article on this:
http://www.ibm.com/developerworks/library/x-xformsniem/
One other technique is to use XQuery to transform XML Schemas. These techniques are described here:
http://en.wikibooks.org/wiki/XRX/XForms_Generator
I look forward to reading your other articles.
- Dan
Hi Alex !!! Thanks for your
Hi Alex !!!
Thanks for your article ...Actually we are doing the same xforms research with plain xforms code with Firefox plug-in.
Developed some sample applications and tested , it's working fine in Firefox ....
In terms of validation part the Schema approach is nice instead of the XSLT , because the code lengthy and all matched tag should need in template section .....
Keep it up your work in XForms
Thanks and Regards
Rajamani Marimuthu
Junior Research Fellow
Open Technology Centre
India.
Thanks for taking the time to
Thanks for taking the time to read and comment Dan.
With regards to the generating forms from XML Schemas, I believe one of my colleagues, Phil Booth, was writing a 'schema analyser' to achieve exactly that. Hopefully sometime in the future he'll contribute an article documenting his experiences.
Cheers,
Alex
Thank you for this
Thank you for this interesting article.
It wouldn't be difficult to integrate it in XSLTForms because the nodeset() XPath extension function is available in almost every browser (except FF2).
Of course, it would be easier to generate an XForms document from an XML Schema. Because it would even nicer with type and repeat recognition so controls would be more pertinent and triggers could be added to add/remove an item.
Alain Couthures