-1

Please help on XSLT, I have this XML sample

<?xml version="1.0" encoding="utf-8"?>
<Document>
  <TopLevel>
    <Header>
      <!-- Header Information-->
    </Header>
    <!--Payments is a one to many-->
    <Payments>
      <PaymentID>PID1</PaymentID>
      <!--More Info-->
      <!--Transactrion can be one to many for each payment-->
      <Transaction>
        <TranssID>TR1 - PID1</TranssID>
        <TranssID>TR2 - PID1</TranssID>
        <!--More Info-->
      </Transaction>
    </Payments>
    <Payments>
      <PaymentID>PID2</PaymentID>
      <!--More Info-->
      <!--Transactrion can be one to many for each payment-->
      <Transaction>
        <TranssID>TR1 - PID2</TranssID>
        <TranssID>TR2 - PID2</TranssID>
        <!--More Info-->
      </Transaction>
    </Payments>
  </TopLevel>
</Document>

I need a looping mechanism in XSLT that will give me the transactions for each payments.

Here is the output when I tried to use the template approach OUTPUT:

<?xml version="1.0" encoding="utf-8"?>
<TopLevel>
  <Payments>
    <PaymentID>PID1</PaymentID>
    <transIDs>TR1 - PID1</transIDs>
    <transIDs>TR2 - PID1</transIDs>
    <transIDs>TR1 - PID2</transIDs>
    <transIDs>TR2 - PID2</transIDs>
    <transIDs>TR1 - PID1</transIDs>
    <transIDs>TR2 - PID1</transIDs>
    <transIDs>TR1 - PID2</transIDs>
    <transIDs>TR2 - PID2</transIDs>
  </Payments>
  <Payments>
    <PaymentID>PID1</PaymentID>
    <transIDs>TR1 - PID1</transIDs>
    <transIDs>TR2 - PID1</transIDs>
    <transIDs>TR1 - PID2</transIDs>
    <transIDs>TR2 - PID2</transIDs>
    <transIDs>TR1 - PID1</transIDs>
    <transIDs>TR2 - PID1</transIDs>
    <transIDs>TR1 - PID2</transIDs>
    <transIDs>TR2 - PID2</transIDs>
  </Payments>
</TopLevel>

I want it to be separated by transaction and by payments

Here is my 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="/">
    <TopLevel>
      <xsl:apply-templates select="//TopLevel/Payments"/>
    </TopLevel>
  </xsl:template>

  <xsl:template match="//TopLevel/Payments">
    <Payments>
      <PaymentID>
        <xsl:value-of select="//TopLevel/Payments/PaymentID"/>
      </PaymentID>
      <xsl:apply-templates select="//TopLevel/Payments/Transaction"/>
    </Payments>
  </xsl:template>

  <xsl:template match="//TopLevel/Payments/Transaction">
    <xsl:apply-templates select="//TopLevel/Payments/Transaction/TranssID"/>
  </xsl:template>

  <xsl:template match="//TopLevel/Payments/Transaction/TranssID">
    <transIDs>
      <xsl:value-of select="."/>
    </transIDs>
  </xsl:template>

</xsl:stylesheet>
zyberjock
  • 337
  • 5
  • 19

2 Answers2

1

Your problem is the way you're using absolute paths (with a leading /) in your apply-templates and value-of instructions. For example

<xsl:apply-templates select="//TopLevel/Payments/Transaction"/>

will apply templates to all Transaction elements in all Payments elements in the whole document. This is why you're seeing every TranssID every time. You should use relative paths so you only extract the sub-elements from within the element you're currently working with, and also you should avoid using // unless it's really necessary, for efficiency reasons:

<?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="/">
    <TopLevel>
      <xsl:apply-templates select="Document/TopLevel/Payments"/>
    </TopLevel>
  </xsl:template>

  <xsl:template match="Payments">
    <Payments>
      <PaymentID>
        <xsl:value-of select="PaymentID"/>
      </PaymentID>
      <xsl:apply-templates select="Transaction"/>
    </Payments>
  </xsl:template>

  <xsl:template match="Transaction">
    <xsl:apply-templates select="TranssID"/>
  </xsl:template>

  <xsl:template match="TranssID">
    <transIDs>
      <xsl:value-of select="."/>
    </transIDs>
  </xsl:template>

</xsl:stylesheet>

Note also that template match expressions don't need the full path back to the root, they just need enough detail to disambiguate between templates. In your case all the different elements you're dealing with have different names, so a single element name is enough to match.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
0

Have you tried anything yet? Before you use <xsl:for-each> you should go with a <xsl:apply-templates> construction (also see: For loops vs. apply-templates).

If you apply 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 method="xml" indent="yes"/>

<xsl:template match="/">
    <list>
        <xsl:apply-templates select="//Payments"/>
    </list>
</xsl:template>

<xsl:template match="Payments">
        <xsl:apply-templates select="Transaction"/>
</xsl:template>

<xsl:template match="Transaction">
    <xsl:apply-templates select="TranssID"/>
</xsl:template>

<xsl:template match="TranssID">
    <transIDs>
        <xsl:value-of select="."/>
    </transIDs>
</xsl:template>

</xsl:stylesheet>

to your source XML you get this output:

<?xml version="1.0" encoding="UTF-8"?>
<list>
 <transIDs>TR1 - PID1</transIDs>
 <transIDs>TR2 - PID1</transIDs>
 <transIDs>TR1 - PID2</transIDs>
 <transIDs>TR2 - PID2</transIDs>
</list>
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Peter
  • 1,786
  • 4
  • 21
  • 40
  • And most importantly, be cautious whenever you encounter phrases like 'you **should** (or **shouldn't**) do that'. ;-) – proskor Aug 27 '13 at 09:04
  • Hello Proskor, in order to set up easily modifiable and easy to read XSLTs - yes one "should" use constructions. Better start using those from the start ;-) Best regards, Peter – Peter Aug 27 '13 at 09:14
  • I have tried the – zyberjock Aug 27 '13 at 09:32
  • @Peter Right. So... why is `for-each` in the specification anyway? Historical accident? – proskor Aug 27 '13 at 09:46
  • 3
    @proskor `for-each` is useful and can make for clearer code in some situations, I think Peter's point is that you should learn when it is appropriate to use it and when it is not. The power of template matching is that it cleanly separates _which nodes_ you're processing from _what_ you want to do to each node. With a `for-each` you have to apply the same logic to every node you `select` and thus stylesheets written by beginners will often end up with complex nests of `choose` constructs to implement logic that the template matcher could have done for you. – Ian Roberts Aug 27 '13 at 09:58
  • @IanRoberts Very well explained! The problem with Peter's statement is that it suggested that you should **always** prefer `apply-templates` over `for-each` (quote: 'Better start using those from the start'), which, of course, is just wrong. – proskor Aug 27 '13 at 10:05