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

时间:2022-03-02 19:16:56

I am new to XSL and in the process of picking this up from scratch to solve a problem.

我是XSL的新手,在从头开始解决问题的过程中。

I have a source XML file that contains the following structure:-

我有一个源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>

where there are mutiple <DetailRecord>s each with multiple <DetailSubRecord>s.

其中有多个 ,每个都带有多个

I have managed to put together an XSL that outputs a single nested set of multiple DetailRecords to a flat file but I haven't been able to puzzle out how to refer to the 2nd nested level of address records in the XSL...

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

Here is my XSL so far:

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

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

2 个解决方案

#1


4  

XSLT is a functional language, not a procedural; what most newcomers to XSLT don't realise is that the XSLT processor automatically handles each node in the tree, in the order they appear in the source. Without a template to define what to do with each node however, nothing is output.

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

In most cases, you don't need to use <xsl:for-each> just to get the child elements processed, this is already done for you, you just need to define a template that describes how you want each element to be output. Like this:

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

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

The <xsl:apply-templates /> in the first template just tells the XSLT processor to deal with the child elements, after which it adds in the record count.

第一个模板中的 只是告诉XSLT处理器处理子元素,之后它会添加记录计数。

The second template handles any element with the three names in it's match atrtibute, and in each case outputs the content (.) concatenated with a new line.

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

The third template in it's current form is actually superfluous, the processor will do that anyway, but you can replace that comment with something more useful.

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

You'll notice this doesn't give any information on how to handle a DetailRecord element; because all you want to do is process it's children, you don't need to specify anything, as that's taken as a given.

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

#2


3  

Here you have a simple example on how to (literally) apply templates to your situation. Because you wasnt so clear about the required output, I've invented one.

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


XSLT 1.0 tested under Saxon 6.5.5

XSLT 1.0在Saxon 6.5.5下测试

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

Applied on your input gets:

应用于您的输入获取:

Customer 1
-London
-Hull
Address Count:2

Customer 2
-Birmingham
-Manchester
Address Count:2

Customer Count:2

#1


4  

XSLT is a functional language, not a procedural; what most newcomers to XSLT don't realise is that the XSLT processor automatically handles each node in the tree, in the order they appear in the source. Without a template to define what to do with each node however, nothing is output.

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

In most cases, you don't need to use <xsl:for-each> just to get the child elements processed, this is already done for you, you just need to define a template that describes how you want each element to be output. Like this:

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

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

The <xsl:apply-templates /> in the first template just tells the XSLT processor to deal with the child elements, after which it adds in the record count.

第一个模板中的 只是告诉XSLT处理器处理子元素,之后它会添加记录计数。

The second template handles any element with the three names in it's match atrtibute, and in each case outputs the content (.) concatenated with a new line.

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

The third template in it's current form is actually superfluous, the processor will do that anyway, but you can replace that comment with something more useful.

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

You'll notice this doesn't give any information on how to handle a DetailRecord element; because all you want to do is process it's children, you don't need to specify anything, as that's taken as a given.

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

#2


3  

Here you have a simple example on how to (literally) apply templates to your situation. Because you wasnt so clear about the required output, I've invented one.

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


XSLT 1.0 tested under Saxon 6.5.5

XSLT 1.0在Saxon 6.5.5下测试

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

Applied on your input gets:

应用于您的输入获取:

Customer 1
-London
-Hull
Address Count:2

Customer 2
-Birmingham
-Manchester
Address Count:2

Customer Count:2