XSL标头和详细信息分组

时间:2014-02-15 00:27:55

标签: xml xslt-1.0

我正在尝试从包含多个标题行的文件创建一组xml,后面跟着每个标题的多个详细信息行。

以下是原始XML的示例:

编辑:页眉页后面的详细信息表数量会有所不同。因此,Sheet [4] / FIELD1并不总是具有Header的值。此外,该文件将始终具有一个或多个标题表。表格是行。

编辑:XSLT-1.0

<Root>
    <Sheet>
        <FIELD1>Header</FIELD1>
        <FIELD2>Value1</FIELD2>
        <FIELD3>Value2</FIELD3>
    </Sheet>
    <Sheet>
         <FIELD1>Detail</FIELD1>
         <FIELD2>Value3</FIELD2>
         <FIELD3>Value4</FIELD3>
      </Sheet>
     <Sheet>
          <FIELD1>Detail</FIELD1>
          <FIELD2>Value5</FIELD2>
          <FIELD3>Value6</FIELD3>
      </Sheet>
     <Sheet>
          <FIELD1>Header</FIELD1>
          <FIELD2>Value7</FIELD2>
          <FIELD3>Value8</FIELD3>
      </Sheet>
     <Sheet>
          <FIELD1>Detail</FIELD1>
          <FIELD2>Value9</FIELD2>
          <FIELD3>Value10</FIELD3>
      </Sheet>
     <Sheet>
          <FIELD1>Detail</FIELD1>
          <FIELD2>Value11</FIELD2>
          <FIELD3>Value12</FIELD3>
      </Sheet>
     <Sheet>
          <FIELD1>Detail</FIELD1>
          <FIELD2>Value13</FIELD2>
          <FIELD3>Value14</FIELD3>
    </Sheet>
 </Root>

以下是输出应该类似的内容:

编辑:发票数量将由标题表(行)数决定。 LineItem行将与标题后面的明细表相同。

<Root>
    <Invoice AttribOne="Value1" AttribTwo="Value2" >
        <LineItem LIAttribOne="Value3" LIAttribTwo="Value4"/>
        <LineItem LIAttribOne="Value5" LIAttribTwo="Value6"/>
    </Invoice>
    <Invoice AttribOne="Value7" AttribTwo="Value8" >
        <LineItem LIAttribOne="Value9" LIAttribTwo="Value10"/>
        <LineItem LIAttribOne="Value11" LIAttribTwo="Value12"/>
        <LineItem LIAttribOne="Value13" LIAttribTwo="Value14"/>
    </Invoice>
</Root>

编辑:在下面回答:

2 个答案:

答案 0 :(得分:0)

试试这个:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>

    <xsl:template match="Root">
        <xsl:copy>
            <Invoice AttribOne="{Sheet[1]/FIELD2}" AttribTwo="{Sheet[1]/FIELD3}">
                <xsl:apply-templates select="Sheet[2] | Sheet[3]"/>
            </Invoice>
            <Invoice AttribOne="{Sheet[4]/FIELD2}" AttribTwo="{Sheet[4]/FIELD3}">
                <xsl:apply-templates select="Sheet[5] | Sheet[6] | Sheet[7]"/>
            </Invoice>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Sheet">
        <LineItem LIAttribOne="{FIELD2}" LIAttribTwo="{FIELD3}"/>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

我已经解决了这个问题,并在下面的代码中添加了评论来描述它。

以下是解决方案:

<xsl:template match="/Root">
    <Root>
         <xsl:apply-templates select="Sheet[FIELD1 = 'Header']" />
    </Root>
</xsl:template>

<xsl:template match="Sheet[FIELD1 = 'Detail']" >
    <LineItem>
      <xsl:attribute name="LIAttribOne" >
        <xsl:value-of select="FIELD2"/>
      </xsl:attribute>
      <xsl:attribute name="LIAttribTwo" >
        <xsl:value-of select="FIELD3"/>
      </xsl:attribute>
    </LineItem>
</xsl:template>

<xsl:template  match="Sheet[FIELD1 = 'Header']">
    <Invoice>
      <xsl:attribute name="AttribOne" >
        <xsl:value-of select="FIELD2"/>
      </xsl:attribute>
      <xsl:attribute name="AttribTwo" >
        <xsl:value-of select="FIELD3"/>
      </xsl:attribute>
  <!-- Get the count of all details the previous headers contained -->
  <xsl:variable name="prevCnt" select="count(preceding-sibling::*[FIELD1 = 'Detail'])"/>
  <!-- Get the count of all details in the previous headers and this header contain -->
  <xsl:variable name="newCnt" select="count(following-sibling::*[FIELD1 = 'Header'][1]/preceding-sibling::*[FIELD1 = 'Detail'])"/>
  <!-- This will return a 1 if there is a following header, else 0 -->
  <xsl:variable name="pos" select="count(following-sibling::*[FIELD1 = 'Header'][1])" />
  <!-- select every detail whose position is less than $newCnt - $preCnt or all if there is no following header -->
  <xsl:apply-templates select="following-sibling::*[FIELD1 = 'Detail'][position() &lt; ($newCnt - $prevCnt + 1) or $pos = 0]"   />
</Invoice>