Reputation: 399
I am using xslt 2.0,
I have the following xml
<Test>
<Examples>
<Example id="uniqueId_1">
<field>test1</field>
</Example>
<Example id="uniqueId_2">
<field>test2</field>
</Example>
</Examples>
<references>
<reference ref="example1">
<value ref="uniqueId_1"/>
</reference>
<reference ref="example2">
<value ref="uniqueId_1"/>
</reference>
</references>
<destinations>
<destination id="example1">
<address> add1 </address>
</destination>
<destination id="example2"/>
</destinations>
</Test>
I need to marshall this to another xml using xslt 2.0
my resulting xml should look like
<Fields>
<Field value="test1" address="add1"/>
<Field value="test2" address="" />
</Fields>
so essentially for each line in examples
I have to see which reference
matches it and derive the destination
from that . I am very confused on how this loop will work . I would do a for-each
on each example
element but how can I compare one example id to multiple references
and then for each reference multiple destinatons
?
Upvotes: 0
Views: 1423
Reputation:
You are thinking in imperative/procedural terms. That's not how XSLT works. In XSLT you don't write commands or loops, you describe the output you want using rules and predicates.
Here, you'll want a rule ("template") matching field
elements. All else being equal, this will be invoked for any field
element. Inside it, you describe the output you want.
<xsl:template match="field">
<!-- emit a Field element -->
<Field>
<!-- its "value" attribute is equal to the field element's content -->
<xsl:attribute name="value" select="."/>
<!-- compute its `address` attribute -->
<xsl:attribute name="address">
<!-- get the id from the parent element (Example) of the field -->
<xsl:variable name="field_id" select="../@id"/>
Now comes an example of the "predicate" I mentioned. Elements can be filtered/searched for with the square bracket syntax []
. The //
notation starts us off with all references
elements in the entire document (we could also have written /Test/references
). Then we get all their reference
children, then, with the square brackets, select the ones of those whose value
child's ref
attribute matches the field_id
we calculated above.
<!-- find reference elt whose "value" child's ref attribute matches -->
<xsl:variable name="reference"
select="//references/reference[value/@ref = $field_id]"/>
<!-- find the destination whose id matches the reference's ref -->
<xsl:variable name="destination"
select="//destinations/destination[@id = $reference/@ref"/>
<!-- pull destination's address and set as value of 'address' att -->
<xsl:value-of select="$destination/address"/>
</xsl:attribute>
</Field>
</xsl:template>
Write another template for the root element ("/") which emits the Fields
element and scans the top-level children:
<xsl:template match="/">
<Fields>
<xsl:apply-children/>
</Fields>
</xsl:template>
The above could be optimized by pre-building maps using XSL features such as xsl:key
, but we'll leave that for another time.
It takes some time to get your head around XSLT. If you find yourself using for-each
or if
or choose
very much in XSLT, you're probably doing something wrong. You could say that the looping and conditional logic is built into XSLT; it does it for you. or to be more precise, it's what it does for a living, it's its raison d'etre. A rule "loops over" input elements, if you want to look at it that way, and processes the ones that apply. A predicate such as foo[@id='id']
"loops over" children and finds those matching the condition that their id is equal to id
.
Upvotes: 2