Rahul Kumar
Rahul Kumar

Reputation: 399

multiple condition check in xslt

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

Answers (1)

user663031
user663031

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.

Note on XSLT

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

Related Questions