4

i am beginner in XSLT and i am using it to transform XML to XML

This is the source XML i receive

Source XML:

<Response>
    <Pax>
        <Id>1</Id>
    </Pax>
    <Pax>
        <Id>2</Id>
    </Pax>
    <Travelers>
        <Traveler>
            <Name>ABC</Name>
        </Traveler>
        <Traveler>
            <Name>XYZ</Name>
        </Traveler>
    </Travelers>
</Response>

I have written below XSLT

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

    <xsl:output method="xml" indent="yes" />

    <xsl:template match="Response">
        <xsl:element name="Root">
                <xsl:apply-templates select="Travelers/Traveler"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="Traveler">
          <xsl:element name="Person">
             <xsl:element name="PId">
                   <xsl:value-of select="//Pax/Id[position()]" />
             </xsl:element>
             <xsl:element name="Name">
                   <xsl:value-of select="Name" />
             </xsl:element>
          </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Output:

<Root>
  <Person>
    <PId>1</PId>
    <Name>ABC</Name>
  </Person>
  <Person>
    <PId>1</PId>
    <Name>XYZ</Name>
  </Person>
</Root>

I would like to generate below XML output

Expected Output:

<Root>
  <Person>
    <PId>1</PId>
    <Name>ABC</Name>
  </Person>
  <Person>
    <PId>2</PId>
    <Name>XYZ</Name>
  </Person>
</Root>

As shown in above XML the only issue is with PId, it should have value 2.

Please help. Thanks.

Ankur Raiyani
  • 1,509
  • 5
  • 21
  • 49

3 Answers3

5

Here's a relatively simple solution.

When this XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output omit-xml-declaration="yes" indent="yes" />
  <xsl:strip-space elements="*" />

  <xsl:template match="/*">
    <Root>
      <xsl:apply-templates select="Pax" />
    </Root>
  </xsl:template>

  <xsl:template match="Pax">
    <xsl:variable name="vPosition" select="position()" />
    <Person>
      <PId>
        <xsl:value-of select="Id" />
      </PId>
      <Name>
        <xsl:value-of select="/*/Travelers/*[$vPosition]/Name" />
      </Name>
    </Person>
  </xsl:template>
</xsl:stylesheet>

...is applied to the original XML:

<Response>
  <Pax>
    <Id>1</Id>
  </Pax>
  <Pax>
    <Id>2</Id>
  </Pax>
  <Travelers>
    <Traveler>
      <Name>ABC</Name>
    </Traveler>
    <Traveler>
      <Name>XYZ</Name>
    </Traveler>
  </Travelers>
</Response>

...the wanted result is produced:

<Root>
  <Person>
    <PId>1</PId>
    <Name>ABC</Name>
  </Person>
  <Person>
    <PId>2</PId>
    <Name>XYZ</Name>
  </Person>
</Root>
ABach
  • 3,743
  • 5
  • 25
  • 33
  • Correct. Usually you can circumvent this issue using `current()`, i.e. `//Pax/Id[@id = current()/@id]` if you had those attributes. However you can't combine `position()` with `current()` and have to resort to using variables. – Nils Werner Nov 28 '12 at 15:04
  • @AnkurRaiyani - only slightly. Notice in my answer how I store `position()` as a variable and then use it later (in my XPath). Using a similar concept in your XPath would most likely help. For more information on using `position()` inside XPath predicates, I would recommend you read about XPath context positions. – ABach Nov 29 '12 at 15:12
  • @ABach - Yes i saw the variable for `position()`. I got your point. When we display value of `position()` it prints correct but as index it doesn't work properly. For that we have to use a variable. So can we say that it is better to use variable in XPath rather the `position()`? – Ankur Raiyani Nov 30 '12 at 03:41
  • @AnkurRaiyani - not exclusively. Using `position()` in `[]`s (which is to say "Using `position()` inside XPath predicates") can be tricky because the value that is output is dependent on the current document position. In my answer above, if I used `position()` inside of the predicate, I would get `ABC` for every ``, no matter what. This is because of the current document position: `position()` would no longer apply to the `` element we were on; rather, it would apply to the `` element we were on (which, with this XPath, is always the first `` matched). – ABach Nov 30 '12 at 15:40
  • 1
    @ABach - Thanks for the clarification. Yes i think now its clear to me. When we specify the `position()` in `[]` it possible that it takes the position of element specified in XPath instead of the loop or template in which we are on. Please correct me if i am wrong. – Ankur Raiyani Dec 03 '12 at 08:18
  • @AnkurRaiyani - you've got it. Take a look at this article for more information: http://www.informit.com/articles/article.aspx?p=102644 – ABach Dec 03 '12 at 15:31
2
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:template match="Response">
    <Root>
      <xsl:for-each select="Travelers/Traveler">
        <Person>
          <xsl:variable name="index" select="position()" />
          <Pid><xsl:value-of select="//Pax[$index]/Id"/></Pid>
          <Name><xsl:value-of select="Name"/></Name>
        </Person>
      </xsl:for-each>
    </Root>
  </xsl:template>
</xsl:stylesheet>
xiaoyi
  • 6,641
  • 1
  • 34
  • 51
0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml"/>
    <xsl:template match="/Response">
        <Root>
            <xsl:for-each select="Pax">
                <xsl:variable name="pos" select="position()"/>
                <Person>
                    <PId>
                        <xsl:value-of select="Id"/>
                    </PId>
                    <xsl:apply-templates select="//Travelers">
                        <xsl:with-param name="pos" select="$pos"/>
                    </xsl:apply-templates>
                </Person>
            </xsl:for-each>
        </Root>
    </xsl:template>
    <xsl:template match="Travelers">
        <xsl:param name="pos"/>
        <xsl:for-each select="//Name">
            <xsl:if test="position()=$pos">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
Shil
  • 211
  • 1
  • 3
  • 11