如何使用 xslt 创建层次结构

How create hierarchy using xslt

我是 XSL 的新手,正在寻找解决问题的方法。我有 xml 类似的东西:

<Table>
  <Row Id="1">
    <Field1>"P_907"</Field1>
    <Field2>"5912"</Field2>
    <Field3>"2013/05/31"</Field3>
    <Field4>"2013/05/31"</Field4>
  </Row>
  <Row Id="2">
    <Field1>"2.1.1.M5"</Field1>
  </Row>
  <Row Id="3">
    <Field1>"3.1.1.M5"</Field1>
  </Row>
  <Row Id="4">
    <Field1>"P_908"</Field1>
    <Field2>"5913"</Field2>
    <Field3>"2013/05/31"</Field3>
    <Field4>"2013/05/31"</Field4>
  </Row>
  <Row Id="5">
    <Field1>"3.11.M2"</Field1>
  </Row>
</Table>

其中行 ID=1 和行 ID=4 是 header 发票,其余行是发票行。每张发票 header 在字段 1 中都有其 ID,但发票行中没有发票 ID。我知道当行中没有 field3 时,这意味着该行是发票行。在其他情况下,它是发票 header。 header 行之前的每一行都属于前 header 行。如何使用 xslt 创建具有适当发票层次结构的 xml?

输出 xml 可能是这样的:

<Invoice>
    <Field1>"P_907"</Field1>
    <Field2>"5912"</Field2>
    <Field3>"2013/05/31"</Field3>
    <Field4>"2013/05/31"</Field4>
  <Row>
    <Field1>"2.1.1.M5"</Field1>
  </Row>
  <Row>
    <Field1>"3.1.1.M5"</Field1>
  </Row>
</Invoice>
<Invoice>
    <Field1>"P_908"</Field1>
    <Field2>"5913"</Field2>
    <Field3>"2013/05/31"</Field3>
    <Field4>"2013/05/31"</Field4>
  <Row>
    <Field1>"3.11.M2"</Field1>
  </Row>
</Invoice>

一种解决方案是以下 XSLT:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"  omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
  <xsl:strip-space elements="*"/>
  <xsl:template match="Table">
    <xsl:apply-templates />
  </xsl:template>
  <xsl:template match="Row[Field3]">
  <xsl:variable name="invoice-count" select="count(preceding-sibling::Row[Field3]) + 1"/>
   <Invoice>
     <xsl:apply-templates/>
     <xsl:apply-templates select="following-sibling::Row[not(Field3) 
    and not(count(preceding-sibling::Row[Field3]) &gt; $invoice-count)]" mode="copy"/>
    </Invoice>
  </xsl:template>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="Row" mode="copy">
    <xsl:copy>
      <xsl:apply-templates select="*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="Row"/>
</xsl:transform>

当应用于您的输入时 XML 产生输出

<Invoice>
  <Field1>"P_907"</Field1>
  <Field2>"5912"</Field2>
  <Field3>"2013/05/31"</Field3>
  <Field4>"2013/05/31"</Field4>
  <Row>
    <Field1>"2.1.1.M5"</Field1>
  </Row>
  <Row>
    <Field1>"3.1.1.M5"</Field1>
  </Row>
</Invoice>
<Invoice>
  <Field1>"P_908"</Field1>
  <Field2>"5913"</Field2>
  <Field3>"2013/05/31"</Field3>
  <Field4>"2013/05/31"</Field4>
  <Row>
    <Field1>"3.11.M2"</Field1>
  </Row>
</Invoice>

一个模板匹配包含 Field3:

的所有 Row 个元素
<xsl:template match="Row[Field3]">

此模板写入一个 <Invoice> 节点并通过应用模板复制此 Row 的内容。然后,通过应用模板 Row 复制所有没有 Field3 且前面带有 Field3 的兄弟 Row 元素不多于当前 Row 的所有后续 silbing Row 元素=22=].
此模板复制 Row 的内容但不复制属性,因此将从输出中删除 Rowid。 为避免两次写入 Row 元素,空模板 <xsl:template match="Row"/> 匹配已通过在模板中应用模板处理的所有 Row 节点,该模板将 Row 元素与 Field3.

相匹配

我会使用以下键来执行此操作:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml"  omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
    <xsl:strip-space elements="*"/>
    <xsl:key name="Rows" match="Row[not(Field3)]" use="generate-id(preceding-sibling::Row[Field3][1])"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Row[Field3]">
        <Invoice>
            <xsl:apply-templates select="node()"/>
            <xsl:apply-templates select="key('Rows', generate-id())" mode="followingRows"/>
        </Invoice>
    </xsl:template>

    <xsl:template match="Row" mode="followingRows">
        <xsl:copy><xsl:apply-templates select="node()"/></xsl:copy>
    </xsl:template>

    <xsl:template match="Row"/>

</xsl:stylesheet>