在下一个节点的xsl中重用旧节点的变量

时间:2019-07-26 10:58:29

标签: xml xslt

我有一个带有物料清单的xml文件,必须将其转换为文本文件以进行进一步处理。 xml文件具有一种方法,如果在“行”节点中“数量” = 0并且“组件”为空,则应该将“父级”的值用于接下来的行,直到找到下一个“父级”行。在这些组件行中,必须跳过“父级”的值。

我正在用变量或参数来存储@Parent的值以进行进一步处理。我发现xslt不支持;-)

有人可以给我提示如何处理此xml文件吗?我可以使用什么方法?我已经阅读了很多带有参数和递归处理的命名模板,但是找不到适合我情况的正确方法。

我在for-each循环中进行了尝试,并能够将其存储在变量中。

示例非常简化的输入XML

<Page>
  <Line>
    <Quantity>0</Quantity>
    <Component></Component>
    <Parent>ParentID1</Parent>
  </Line>
  <Line>
    <Quantity>4</Quantity>
    <Component>ComponentID1</Component>
    <Parent>YYY</Parent>
  </Line>
  <Line>
    <Quantity>2</Quantity>
    <Component>ComponentID2</Component>
    <Parent>ZZZ</Parent>
  </Line>
  <Line>
    <Quantity>0</Quantity>
    <Component></Component>
    <Parent>ParentID2</Parent>
  </Line>
  <Line>
    <Quantity>3</Quantity>
    <Component>ComponentID4</Component>
    <Parent>AAA</Parent>
  </Line>
  <Line>
    <Quantity>2</Quantity>
    <Component>ComponentID5</Component>
    <Parent>XXX</Parent>
  </Line>
<Page>

例如xml的预期输出(可以丢弃标题)

#Parent~Component~Quantity
ParentID1~ComponentID1~4
ParentID1~ComponentID2~2
ParentID2~ComponentID4~3
ParentID2~ComponentID5~2

2 个答案:

答案 0 :(得分:0)

如果我正确理解这一点,则可以这样做:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />

<xsl:key name="cpmt" match="Line[Component/text()]" use="generate-id(preceding-sibling::Line[not(Component/text())][1])" />

<xsl:template match="/Page">
    <xsl:for-each select="Line[not(Component/text())]">
        <xsl:variable name="parent" select="Parent" />
        <xsl:for-each select="key('cpmt', generate-id())">
            <xsl:value-of select="$parent"/>
            <xsl:text>-</xsl:text>
            <xsl:value-of select="Component"/>
            <xsl:text>-</xsl:text>
            <xsl:value-of select="Quantity"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each> 
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

请注意,我省略了Quantity = 0的要求;如果至关重要,则可以将其添加到谓词中。


如果您的处理器支持XSLT 2.0,那么使用xsl:for-each-group可以更容易:

XSLT 2.0

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />

<xsl:template match="/Page">
    <xsl:for-each-group select="Line" group-starting-with="Line[not(Component/text())]">
        <xsl:variable name="parent" select="Parent" />
        <xsl:for-each select="current-group()[position() > 1]">
            <xsl:value-of select="$parent, Component, Quantity" separator="-"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each> 
    </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

这是XSLT 2/3的替代方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:output method="text" />

  <xsl:template match="Page">
     <xsl:for-each-group select="Line" group-starting-with="Line[Quantity = 0 and not(normalize-space(Component))]">
         <xsl:value-of select="tail(current-group())/string-join((current()/Parent, Component, Quantity), '~')" separator="&#10;"/>
         <xsl:text>&#10;</xsl:text>
     </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/94rmq7c