Frage

I'm facing an issue with the xslt and have no idea how to transform the below XML in to desired output. How to read the elements of similar nodes and generate the output in same line based on a condition? Also, The O/P File has predefined format.

XML

<?xml version="1.0" encoding="UTF-8"?>
<Plan Id="01">
      <Ledger>1</Ledger>
      <Product>PRD-AAA</Product>      
      <Order_Amount>2480</Order_Amount>
      <Invoice_Amount>496</Invoice_Amount>
</Plan>
<Plan Id="02">
      <Ledger>2</Ledger>
      <Product>PRD-BBB</Product>      
      <Order_Amount>9000</Order_Amount>
      <Invoice_Amount>3000</Invoice_Amount>
</Plan>
<Plan Id="03">
      <Product Id="PRD-X">
         <Ledger>1</Ledger>  
         <Order_Amount>4500</Order_Amount>
     <Invoice_Amount>1650</Invoice_Amount>
      </Product>
      <Product Id="PRD-Y">
     <Ledger>1</Ledger>         
         <Order_Amount>550</Order_Amount>
         <Invoice_Amount>866</Invoice_Amount>
      </Product>
      <Product Id="PRD-Z">
         <Ledger>2</Ledger>  
         <Order_Amount>5000</Order_Amount>
         <Invoice_Amount>1000</Invoice_Amount>
      </Product>
      <Product Id="PRD-A">
         <Ledger>3</Ledger>  
         <Order_Amount>2500</Order_Amount>
         <Invoice_Amount>1000</Invoice_Amount>
      </Product>
</Plan>

Columns of the CSV files are (No need to print the column header) this is just to understand the O/P file format. Plan Details,Plan ID,Ledger#,Product Code,Amounts,Order Amount,Invoice Amount,STATICTEXTFILLER1,STATICTEXTFILLER2,,Plan ID,Ledger#,Product Code,Amounts,Order Amount,Invoice Amount,STATICTEXTFILLER3

Rules: 1) STATICTEXTFILLER1,STATICTEXTFILLER2 will come after the first Invoice amount

2) STATICTEXTFILLER13 is at the end of the last Invoice amount

3) If plan 03 had only one product in Ledger# the remaining details on the file should left blank except the STATICTEXTFILLER1,STATICTEXTFILLER3,STATICTEXTFILLER3

Expected Output enter image description here Plan Details,01,1,PRD-AAA,Amounts,2480,496,STATICTEXTFILLER1,STATICTEXTFILLER2,,,,Amounts,,,STATICTEXTFILLER3 Plan Details,02,2,PRD-BBB,Amounts,9000,3000,STATICTEXTFILLER1,STATICTEXTFILLER2,,,,Amounts,,,STATICTEXTFILLER3 Plan Details,03,1,PRD-X,Amounts,4500,1650,STATICTEXTFILLER1,STATICTEXTFILLER2,03,1,PRD-Y,Amounts,550,866,STATICTEXTFILLER3 Plan Details,03,2,PRD-Z,Amounts,5000,1000,STATICTEXTFILLER1,STATICTEXTFILLER2,,,,Amounts,,,STATICTEXTFILLER3 Plan Details,03,3,PRD-A,Amounts,2500,1000,STATICTEXTFILLER1,STATICTEXTFILLER2,,,,Amounts,,,STATICTEXTFILLER3

War es hilfreich?

Lösung 2

Your source didn't have a root element so I just chose:

Edit: Updated root element as mentioned in comment

<?xml version="1.0" encoding="UTF-8"?>
<Report_Data>
<Plan Id="01">
    <Ledger>1</Ledger>
    <Product>PRD-AAA</Product>      
    <Order_Amount>2480</Order_Amount>
    <Invoice_Amount>496</Invoice_Amount>
</Plan>
<Plan Id="02">
    <Ledger>2</Ledger>
    <Product>PRD-BBB</Product>      
    <Order_Amount>9000</Order_Amount>
    <Invoice_Amount>3000</Invoice_Amount>
</Plan>
<Plan Id="03">
    <Product Id="PRD-X">
        <Ledger>1</Ledger>  
        <Order_Amount>4500</Order_Amount>
        <Invoice_Amount>1650</Invoice_Amount>
    </Product>
    <Product Id="PRD-Y">
        <Ledger>1</Ledger>         
        <Order_Amount>550</Order_Amount>
        <Invoice_Amount>866</Invoice_Amount>
    </Product>
    <Product Id="PRD-Z">
        <Ledger>2</Ledger>  
        <Order_Amount>5000</Order_Amount>
        <Invoice_Amount>1000</Invoice_Amount>
    </Product>
    <Product Id="PRD-A">
        <Ledger>3</Ledger>  
        <Order_Amount>2500</Order_Amount>
        <Invoice_Amount>1000</Invoice_Amount>
    </Product>
</Plan>
</Report_Data>

With this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="text"/>

<xsl:template match="Report_Data">
    <xsl:apply-templates select="Plan"/>
</xsl:template>

<xsl:template match="Plan">
    <xsl:choose>
        <!-- Plan type 1, Id 1 and 2 -->
        <xsl:when test="not(Product/@Id)">
            <xsl:value-of select="@Id,Ledger,Product,Order_Amount,Invoice_Amount" separator=","/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:when>
        <!-- Plan type 2, Id 3-->
        <xsl:otherwise>
            <xsl:for-each-group select="Product" group-by="Ledger">
                <xsl:value-of
                    select="for $i in current-group() return ($i/../@Id,$i/Ledger,$i/@Id,$i/Order_Amount,$i/Invoice_Amount)"
                    separator=","/>
                <xsl:text>&#xA;</xsl:text>
            </xsl:for-each-group>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
</xsl:stylesheet>

I get:

01,1,PRD-AAA,2480,496
02,2,PRD-BBB,9000,3000
03,1,PRD-X,4500,1650,03,1,PRD-Y,550,866
03,2,PRD-Z,5000,1000
03,3,PRD-A,2500,1000

Basically the value-of's select the sequence of elements in the order you want to display them depending which plan type you have. With xslt-2.0 you can save yourself the delimeter and use the value-of's seprartor attribute instead.

Andere Tipps

There are only 3 plans in which Plan 03 had multiple products. Plan 01 and Plan 02 will always only one product.

That's a strange way to organize data. Anyway, try the following:

XSLT 1.0

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

<xsl:key name="product3-by-ledger" match="Plan[@Id='03']/Product" use="Ledger" />

<xsl:variable name="LF" select="'&#xA;'" />
<xsl:variable name="delim" select="','" />

<xsl:template match="/">
    <!-- Plans 01 & 02 -->
    <xsl:for-each select="Report_Data/Plan[@Id='01' or @Id='02']">
        <xsl:value-of select="concat(@Id, $delim, Ledger, $delim, Product, $delim, Order_Amount, $delim, Invoice_Amount, $LF)"/>
    </xsl:for-each>
    <!-- Plan 03 -->
    <!-- for each distinct ledger -->
    <xsl:for-each select="/Report_Data/Plan[@Id='03']/Product[count(. | key('product3-by-ledger', Ledger)[1]) = 1]">
            <!--get products with same ledger -->
        <xsl:for-each select="key('product3-by-ledger', Ledger)">
            <xsl:value-of select="concat(../@Id, $delim, Ledger, $delim, @Id, $delim, Order_Amount, $delim, Invoice_Amount)"/>
        </xsl:for-each>
        <xsl:if test="position()!=last()">
            <xsl:value-of select="$LF"/>
        </xsl:if>
        </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

Edit:
If you are using XSLT 2.0, go with <xsl:for-each-group> as suggested in the other answer, instead of Muenchian grouping.


Added:

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

<xsl:key name="product3-by-ledger" match="Plan[@Id='03']/Product" use="Ledger" />

<xsl:variable name="LF" select="'&#xA;'" />
<xsl:variable name="delim" select="','" />

<xsl:template match="/">
    <!-- Plans 01 & 02 -->
    <xsl:for-each select="Report_Data/Plan[@Id='01' or @Id='02']">
        <xsl:value-of select="concat(@Id, $delim, Ledger, $delim, Product, $delim, Order_Amount, $delim, Invoice_Amount, $delim, 'STATICTEXTFILLER1,STATICTEXTFILLER2,STATICTEXTFILLER3', $LF)"/>
    </xsl:for-each>
    <!-- Plan 03 -->
    <!-- for each distinct ledger -->
    <xsl:for-each select="/Report_Data/Plan[@Id='03']/Product[count(. | key('product3-by-ledger', Ledger)[1]) = 1]">
            <!--get products with same ledger -->
        <xsl:for-each select="key('product3-by-ledger', Ledger)">
            <xsl:value-of select="concat(../@Id, $delim, Ledger, $delim, @Id, $delim, Order_Amount, $delim, Invoice_Amount, $delim)"/>
            <xsl:if test="position()=1">
                <xsl:text>STATICTEXTFILLER1,STATICTEXTFILLER2,</xsl:text>
            </xsl:if>
            <xsl:if test="position()=last()">
                <xsl:text>STATICTEXTFILLER3</xsl:text>
            </xsl:if>
        </xsl:for-each>
        <xsl:if test="position()!=last()">
            <xsl:value-of select="$LF"/>
        </xsl:if>
        </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

The above stylesheet, when applied to a corrected input of:

<Report_Data>
    <Plan Id="01">
          <Ledger>1</Ledger>
          <Product>PRD-AAA</Product>      
          <Order_Amount>2480</Order_Amount>
          <Invoice_Amount>496</Invoice_Amount>
    </Plan>
    <Plan Id="02">
          <Ledger>2</Ledger>
          <Product>PRD-BBB</Product>      
          <Order_Amount>9000</Order_Amount>
          <Invoice_Amount>3000</Invoice_Amount>
    </Plan>
    <Plan Id="03">
          <Product Id="PRD-X">
             <Ledger>1</Ledger>  
             <Order_Amount>4500</Order_Amount>
         <Invoice_Amount>1650</Invoice_Amount>
          </Product>
          <Product Id="PRD-Y">
         <Ledger>1</Ledger>         
             <Order_Amount>550</Order_Amount>
             <Invoice_Amount>866</Invoice_Amount>
          </Product>
          <Product Id="PRD-Z">
             <Ledger>2</Ledger>  
             <Order_Amount>5000</Order_Amount>
             <Invoice_Amount>1000</Invoice_Amount>
          </Product>
          <Product Id="PRD-A">
             <Ledger>3</Ledger>  
             <Order_Amount>2500</Order_Amount>
             <Invoice_Amount>1000</Invoice_Amount>
          </Product>
    </Plan>
</Report_Data>

will produce:

01,1,PRD-AAA,2480,496,STATICTEXTFILLER1,STATICTEXTFILLER2,STATICTEXTFILLER3
02,2,PRD-BBB,9000,3000,STATICTEXTFILLER1,STATICTEXTFILLER2,STATICTEXTFILLER3
03,1,PRD-X,4500,1650,STATICTEXTFILLER1,STATICTEXTFILLER2,03,1,PRD-Y,550,866,STATICTEXTFILLER3
03,2,PRD-Z,5000,1000,STATICTEXTFILLER1,STATICTEXTFILLER2,STATICTEXTFILLER3
03,3,PRD-A,2500,1000,STATICTEXTFILLER1,STATICTEXTFILLER2,STATICTEXTFILLER3
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top