如何使用xslt创建层次结构

时间:2015-02-17 21:36:51

标签: 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>

其中Row Id = 1且Row Id = 4是发票的标题,剩余的行是发票行。每个发票标题的ID都在field1中,但发票行中没有发票ID。我知道当行中没有field3时,表示该行是发票行。在其他情况下,它是发票标题。标题行之前的每一行都属于上一个标题行。如何使用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>

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:-2)

一个解决方案是以下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>

一个模板匹配包含Row的所有Field3元素:

<xsl:template match="Row[Field3]">

此模板编写<Invoice>节点,并通过应用模板复制此Row的内容。然后,通过应用模板{{}}复制所有跟随Row元素且没有Field3元素的Row元素而不是当前Field3的所有元素Row {1}}。
此模板会复制mode="copy"但不会复制属性的内容,因此Row的{​​{1}}将从输出中删除。 为避免两次写id个元素,空模板 Row通过在模板中应用与Row元素<xsl:template match="Row"/>匹配的模板来匹配已经处理的所有Row个节点。