如何使用模板遍历嵌套的XML结构

时间:2011-06-01 08:08:30

标签: xml xslt nested

我是XSL的新手,并且正在从头开始解决问题。

我有一个包含以下结构的源XML文件: -

<root>
  <Header>

  </Header>

  <DetailRecord>
    <CustomerNumber>1</CustomerNumber>
    <DetailSubRecord>
      <Address>London</Address>
    </DetailSubRecord>
    <DetailSubRecord>
      <Address>Hull</Address>
    </DetailSubRecord>

  </DetailRecord>

  <DetailRecord>
    <CustomerNumber>2</CustomerNumber>
    <DetailSubRecord>
      <Address>Birmingham</Address>
    </DetailSubRecord>
    <DetailSubRecord>
      <Address>Manchester</Address>
    </DetailSubRecord>

  </DetailRecord>
  <Footer>

  </Footer>

</root>

其中有多个<DetailRecord>,每个<DetailSubRecord>个多个<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*"/> <xsl:output method="text"/> <xsl:variable name="spaces" select="' '"/> <xsl:variable name="newline"> <xsl:text>&#10;</xsl:text> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="root/Header/HeaderField"/> <xsl:copy-of select="$newline"/> <xsl:for-each select="root/DetailRecord"> <xsl:value-of select="CustomerNumber"/> <xsl:copy-of select="$newline"/> </xsl:for-each> Trailer - recordCount - <xsl:value-of select="count(root/DetailRecord)"/> </xsl:template> </xsl:stylesheet>

我已经设法将一个XSL输出到一个平面文件输出一个嵌套的多个DetailRecords,但是我还没有弄清楚如何在XSL中引用第二个嵌套级别的地址记录。

到目前为止,这是我的XSL:

{{1}}

2 个答案:

答案 0 :(得分:4)

XSLT是一种功能语言,而不是程序性语言; XSLT的大多数新手都没有意识到,XSLT处理器会按照它们在源中出现的顺序自动处理树中的每个节点。如果没有模板来定义如何处理每个节点,则不会输出任何内容。

在大多数情况下,您不需要仅使用<xsl:for-each>来处理子元素,这已经为您完成了,您只需要定义一个模板来描述您希望每个元素是如何处理的输出。像这样:

<xsl:template match="root">
  <xsl:apply-templates />
  <xsl:text>Trailer - recordCount - </xsl:text>
  <xsl:value-of select="count(DetailRecord)" />
</xsl:template>

<xsl:template match="HeaderField | CustomerNumber | Address">
  <xsl:value-of select="concat(.,$newline)" />
</xsl:template>

<xsl:template match="DetailSubRecord">
  <!-- do something with subrecord here -->
  <xsl:apply-templates />
</xsl:template>

第一个模板中的<xsl:apply-templates />只是告诉XSLT处理器处理子元素,之后它会添加记录数。

第二个模板处理任意元素,其中包含三个名称match atrtibute,并且在每种情况下输出与新行连接的内容(.)。

它当前形式的第三个模板实际上是多余的,处理器无论如何都会这样做,但你可以用更有用的东西替换那个评论。

您会注意到这没有提供有关如何处理DetailRecord元素的任何信息;因为你要做的只是处理它的孩子,你不需要指定任何东西,因为这是作为给定的。

答案 1 :(得分:3)

这里有一个关于如何(字面)将模板应用于您的情况的简单示例。因为你对所需的输出并不是那么清楚,所以我发明了一个。


Saxon 6.5.5 下测试

XSLT 1.0

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

    <xsl:strip-space elements="*"/>
    <xsl:output method="text"/>

    <xsl:variable name="spaces" select="' '"/>
    <xsl:variable name="nl">
        <xsl:text>&#10;</xsl:text>
    </xsl:variable>

    <xsl:template match="/root">
        <xsl:apply-templates select="DetailRecord"/>
        <xsl:apply-templates select="Footer"/>
    </xsl:template>

    <xsl:template match="DetailRecord">
        <xsl:value-of select="concat(
            'Customer ',
            CustomerNumber, $nl)"
            />
        <xsl:apply-templates select="DetailSubRecord"/>
        <xsl:value-of select="concat(
            'Address Count:',
            count(DetailSubRecord),$nl,$nl
            )"
          />
    </xsl:template>

    <xsl:template match="DetailSubRecord">
        <xsl:value-of select="concat('-',Address,$nl)"/>
    </xsl:template>

    <xsl:template match="Footer">
        <xsl:value-of select="concat(
            'Customer Count:',
            count(preceding-sibling::DetailRecord),$nl
            )"
            />
    </xsl:template>

</xsl:stylesheet>

应用于您的输入获取:

Customer 1
-London
-Hull
Address Count:2

Customer 2
-Birmingham
-Manchester
Address Count:2

Customer Count:2