How to achieve dynamic binding

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

How to achieve dynamic binding

ogheights
Assuming we have an xml document like the one below.
<data>
   <node1 null="true" type="integer" />
   <node2 null="true" type="date" />
   <node3 null="false" type="string" />
   </data>
 we need to transform this doc into XForms doc. The behaviour task in XForms is placed inside <model> </model> node.
If we write this part by hand it looks like this
<model>
<xforms:bind nodeset="node1" required="true()" type="xs:integer"/>
<xforms:bind nodeset="node2" required="true()" type="xs:date"/>
<xforms:bind nodeset="node3" required="false()" type="xs:string"/>
</model>
But I need to call a template which builds up this part dynamically. i.e. Xslt processor will visit each node in xml document,  read its name as well as its attributes values (null, type), then construct the desired <model> node.
 
The main structure of this template could be like this

<xsl:template match="data">
  <xsl:for-each select="data/*">
    <xsl:value-of select="local-name()"/> 
    <xsl:value-of select="@null" /> 
    <xsl:value-of select="@type" /> 
        
   </xsl:for-each>
  </xsl:template> 

My question is how to build up a generic statement that generates the binding part?

Any help is much appreciated
Reply | Threaded
Open this post in threaded view
|

RE: How to achieve dynamic binding

Klotz, Leigh
Is your question how to make the value of the nodeset or the simpler
question of how to get XSLT to output the bind element?

Here's the simpler answer:

<xsl:template match="data">
  <xsl:for-each select="data/*">
    <xf:bind>
      <xsl:attribute name="nodeset"><xsl:value-of
select="local-name()"/></xsl:attribute>
      <xsl:attribute name="required"><xsl:value-of select="@null"
/>()</xsl:attribute>
      <xsl:attribute name="type"><xsl:value-of select="@type"
/></xsl:attribute>
    </xf:bind>
   </xsl:for-each>
  </xsl:template>

If your processor supports the xf:integer etc. types, then you needn't
add the namespace prefix to the types.
(If it doesn't, then you'll have to bake some knowledge into the
transformation about the namespace prefixes available, because the name
is used inside an attribute value, and the usual XML knowledge about
namespaces doesn't extend to attribute values.)

The harder question arises if your data isn't flat, but has nested or
repeated nodes.  In this case, you need to construct a value for
bind/@nodeset that is more complicated than just local-name().  (Also
note if you use namespaces for the names you'll have to deal with that
too, as locale-name() won't.)  In general, there's no way to
automatically determine XPath for getting to a particular node, because
what you and I define as the node might differ.  For example, if someone
inserts a second node1:
 <data>
   <node1 null="true" type="integer" />
   <node1 null="true" type="double" />
   <node2 null="true" type="date" />
   <node3 null="false" type="string" />
 </data>

The above XSLT fragment won't work because it will generate two binds
for the same node name; even if we fixed that, it would generate
conflicting binds.  So, if you truly had some data that looked like
this, you'd have to decide as a human what you wanted, and XSLT isn't up
to that.  You have to decide the tradeoffs, and one possibility would be
to use positions, so you could write XSLT that produces this:

  <xf:bind nodeset="node1[1]" required="true()" type="integer" />
  <xf:bind nodeset="node1[2]" required="true()" type="double" />

The code for doing that in XSLT isn't that hard; it uses the position()
and count() XPath functions, but it leads to a certain fragility, and
would be necessary only if your data requires it.

If you have simple nested data such as this the answers are simple:

 <data>
     <potato>
      <node1 null="true" type="integer" />
      <node2 null="true" type="date" />
      <node3 null="false" type="string" />
     </potato>
     <tomato>
      <node1 null="true" type="float" />
      <node2 null="false" type="string" />
      <node3 null="false" type="date" />
     </tomato>
 </data>

Use recursive descent to generate
  <xf:bind nodeset="potato">
    <xf:bind nodeset="node1" required="true()" type="integer" />
    <xf:bind nodeset="node2" required="true()" type="date" />
    <xf:bind nodeset="node3" required="false()" type="string" />
</xf:bind>
  <xf:bind nodeset="tomato">
    <xf:bind nodeset="node1" required="true()" type="float" />
    <xf:bind nodeset="node2" required="false()" type="string" />
    <xf:bind nodeset="node3" required="false()" type="date" />
  </xf:bind>

You can also use the ancestor axis to generate this, but it's more
complicated to write.
  <xf:bind nodeset="potato/node1" required="true()" type="integer" />
  <xf:bind nodeset="potato/node2" required="true()" type="date" />
  <xf:bind nodeset="potato/node3" required="false()" type="string" />
  <xf:bind nodeset="tomato/node1" required="true()" type="float" />
  <xf:bind nodeset="tomato/node2" required="false()" type="string" />
  <xf:bind nodeset="tomato/node3" required="false()" type="date" />

Unfortunately, if you have more complex constraints on your data
structure that aren't as easily inferred, you will have difficulty
writing XPath expressions that match particular nodes.

If that's the case, you might consider procesing your original data to
produce an XML Schema to use to apply the type definitions and
incorporate that Schema into your XForms document.

Leigh.

P.S. Do you map null to required directly?  I'd have thought required
was not(null).  There are various ways to do that, of course, but here
is a cheesy one that will work quickly:

      <xsl:attribute name="required">not(<xsl:value-of select="@null"
/>())</xsl:attribute>
 
-----Original Message-----
From: [hidden email]
[mailto:[hidden email]] On Behalf Of
ogheights
Sent: Thursday, February 21, 2008 4:02 AM
To: [hidden email]
Subject: How to achieve dynamic binding


Assuming we have an xml document like the one below.
<data>
   <node1 null="true" type="integer" />
   <node2 null="true" type="date" />
   <node3 null="false" type="string" />
   </data>
 we need to transform this doc into XForms doc. The behaviour task in
XForms
is placed inside <model> </model> node.
If we write this part by hand it looks like this
<model>
<xforms:bind nodeset="node1" required="true()" type="xs:integer"/>
<xforms:bind nodeset="node2" required="true()" type="xs:date"/>
<xforms:bind nodeset="node3" required="false()" type="xs:string"/>
</model>
But I need to call a template which builds up this part dynamically.
i.e.
Xslt processor will visit each node in xml document,  read its name as
well
as its attributes values (null, type), then construct the desired
<model>
node.
 
The main structure of this template could be like this

<xsl:template match="data">
  <xsl:for-each select="data/*">
    <xsl:value-of select="local-name()"/>
    <xsl:value-of select="@null" />
    <xsl:value-of select="@type" />
       <!--  How  to build up  a generic statement which generates the
binding part -->
   </xsl:for-each>
  </xsl:template>

My question is how to build up a generic statement that generates the
binding part?

Any help is much appreciated

--
View this message in context:
http://www.nabble.com/How-to-achieve-dynamic-binding-tp15608160p15608160
.html
Sent from the Mozilla - XForms mailing list archive at Nabble.com.

_______________________________________________
dev-tech-xforms mailing list
[hidden email]
https://lists.mozilla.org/listinfo/dev-tech-xforms
_______________________________________________
dev-tech-xforms mailing list
[hidden email]
https://lists.mozilla.org/listinfo/dev-tech-xforms