【问题标题】:Reuse variable of older node in xsl in next nodes在下一个节点中重用 xsl 中旧节点的变量
【发布时间】:2019-07-26 10:58:29
【问题描述】:

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

我正在努力使用变量或参数来存储 @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 更复杂。我在下面添加了它。我一开始使用的方法是先设置一个变量,但它在 for-each-group 中不起作用。

<xsl:variable name="EPBomQty" >
 <xsl:for-each select="Label/Property">
        <xsl:if test="PropertyName='Quantity'">
            <xsl:value-of select="PropertyValue"/>
        </xsl:if>
    </xsl:for-each> 
</xsl:variable>

新的xml

<Page>
  <Line>
    <Label source_id="1">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>0</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue/>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>ParentID1</PropertyValue>
        </Property>
    </Label>
  </Line>
  <Line>
    <Label source_id="2">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>4</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue>ComponentID1</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>YYY</PropertyValue>
        </Property>
    </Label>
  </Line>
  <Line>
    <Label source_id="3">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>2</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue>ComponentID2</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>ZZZ</PropertyValue>
        </Property>
    </Label>
  </Line>
  <Line>
      <Label source_id="4">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>0</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue/>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>ParentID2</PropertyValue>
        </Property>
    </Label>
  </Line>
  <Line>
     <Label source_id="5">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>3</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue>ComponentID4</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>AAA</PropertyValue>
        </Property>
    </Label>
  </Line>
  <Line>
    <Label source_id="6">
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Quantity</PropertyName>
            <PropertyValue>2</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Component ID</PropertyName>
            <PropertyValue>ComponentID5</PropertyValue>
        </Property>
        <Property FormattingType="0" FormattingLength="32" FormattingRAlign="1">
            <PropertyName>Parent ID</PropertyName>
            <PropertyValue>XXX</PropertyValue>
        </Property>
    </Label>
  </Line>
</Page>

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

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

【问题讨论】:

  • 各种答案建议将此视为分组问题,但另一种方法是,在处理每一行时,使用preceding-sibling::Line[Quantity=0 and Component=''][1]/Parent 获取适当的Parent 值。
  • @MichaelKay 但问题是如何避免重复查找。

标签: xml xslt


【解决方案1】:

这是一个 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

【讨论】:

  • 您好 Martin,我从假期回来,正在尝试实施您的方法。但是由于输入 XML 比我第一次提到的更复杂,所以我仍然失败了。你能再看看吗?
  • @WaltervL,考虑提出一个新问题,尝试将此处提出的建议调整为更复杂的输入。
【解决方案2】:

如果我理解正确,您可以这样做:

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>

补充:

要处理“更复杂”的输入,请尝试:

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/Label" group-starting-with="Label[not(Property[PropertyName='Component ID']/PropertyValue/text())]">
        <xsl:variable name="parent" select="Property[PropertyName='Parent ID']/PropertyValue" />
        <xsl:for-each select="current-group()[position() > 1]">
            <xsl:value-of select="$parent, Property[PropertyName='Component ID']/PropertyValue, Property[PropertyName='Quantity']/PropertyValue" separator="-"/>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each> 
    </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

【讨论】:

  • 你好迈克尔,我从假期回来并尝试实施你的方法。但是由于输入 XML 比我第一次提到的更复杂,我仍然失败了。你能再看看吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多