具有预定义文件格式的同一行的组和子组元素节点

时间:2014-03-26 18:46:25

标签: xml xslt transform xslt-2.0

我遇到了xslt的问题,并且不知道如何将下面的XML转换为所需的输出。 如何根据条件读取相似节点的元素并在同一行生成输出? 此外,O / P文件具有预定义的格式。

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>

CSV文件的列 (无需打印列标题)这只是为了理解O / P文件格式。 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

规则: 1)STATICTEXTFILLER1,STATICTEXTFILLER2将在第一张发票金额后出现

2)STATICTEXTFILLER13位于最后一个发票金额的末尾

3)如果计划03只有一个产品在Ledger#中,文件的其余细节应该留空,除了STATICTEXTFILLER1,STATICTEXTFILLER3,STATICTEXTFILLER3

预期输出 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

2 个答案:

答案 0 :(得分:2)

  

计划03只有3个计划有多个产品。计划01   而计划02将始终只有一个产品。

这是组织数据的一种奇怪方式。无论如何,请尝试以下方法:

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>

修改
如果您使用的是XSLT 2.0,请按照其他答案中的建议使用<xsl:for-each-group>,而不是Muenchian分组。


加了:

<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>

以上样式表应用于更正后的输入:

<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>

将产生:

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

答案 1 :(得分:1)

你的来源没有根元素,所以我选择了:

修改:更新了评论

中提到的根元素
<?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>

有了这个:

<?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>

我明白了:

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

基本上value-of's根据您的计划类型选择要显示的顺序中的元素序列。使用xslt-2.0,您可以自己保存分隔符并使用值的seprartor属性。