假设我有一个这样的XML文档:
<?xml version="1.0" encoding="UTF-8"?>
<everything>
<something>
<part>
<prop type="a">1</prop>
<prop type="b">2</prop>
<prop type="c">3</prop>
</part>
<part>
<prop type="a">4</prop>
<!--No b!-->
<prop type="c">5</prop>
</part>
</something>
<something>
<part>
<prop type="a">6</prop>
<prop type="b">7</prop>
<!--No c!-->
</part>
</something>
</everything>
我想要这样的输出:
<something>
<props type="a">14</props>
<props type="b">2?</props>
<props type="c">35</props>
</something>
<something>
<props type="a">6</props>
<props type="b">7</props>
<props type="c">?</props>
</something>
换句话说,首先我想知道文档中的所有prop/@type
是什么。在这种情况下,'a','b'和'c'。我已经找到了该部分的解决方案,我将为这个问题掩盖。接下来,对于每个“东西”,我想迭代那些预期的prop元素,并将当前“something”元素中该类型的所有道具合并在一个名为props
的新元素下。如果在零件元素中未找到其中一个预期的prop元素,则插入问号。
以下是我最好的尝试。我认为至少有两个问题。首先,在<xsl:for-each select="$allTheProps/prop">̀
循环的范围内,我不能像我通常可以在$ allTheProps循环之外那样select="part"
。另一个问题是我认为你不能在路径模式中有变量,所以我无法测试@type=$nodeType
。最终结果是我的props
元素只是空的。我怎样才能解决这些问题?
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:variable name="allTheProps">
<!--These elements are actually dynamically generated
based on all prop types found in the document-->
<prop type="a"></prop>
<prop type="b"></prop>
<prop type="c"></prop>
</xsl:variable>
<xsl:template match="something">
<xsl:copy>
<!--for each found type-->
<xsl:for-each select="$allTheProps/prop">
<xsl:variable name="nodeType" select="@type"/>
<props>
<xsl:attribute name="type"><xsl:value-of select="$nodeType" />
</xsl:attribute>
<!--merge the props from each part of a something node-->
<xsl:for-each select="part">
<xsl:value-of select="prop[@type=$nodeType]"/>
<xsl:if test="not(prop[@type=$nodeType])">?</xsl:if>
</xsl:for-each>
</props>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:transform>
答案 0 :(得分:1)
在XSLT 3.0中,你可以做到
<xsl:template match="something">
<something>
<xsl:merge>
<xsl:merge-source for-each="part" select="prop">
<xsl:merge-key select="@type">
</xsl:merge-source>
<xsl:merge-action>
<props type="{current-merge-key()}">
<xsl:value-of select="string-join(current-merge-group(), '')"/>
</props>
</xsl:merge-action>
</xsl:merge>
</something>
</xsl:template>
答案 1 :(得分:0)
您忘记了第一个<xsl:for-each select="$allTheProps/prop">
将上下文更改为变量allTheProps
,因此您的下一个<xsl:for-each select="part">
将在此变量的上下文中执行,不在您期望的模板中(part
)。因此,您最终会在for-each
。
解决方案是在变量中保留上下文 - 这里我将其命名为subTree
- 并访问此变量。因此,只需进行两项小修改即可使代码按预期工作。
<xsl:template match="something">
<xsl:variable name="subTree" select="*" /> <!-- save context -->
<xsl:copy>
<!--for each found type-->
<xsl:for-each select="$allTheProps/prop">
<xsl:variable name="nodeType" select="@type"/>
<props>
<xsl:attribute name="type"><xsl:value-of select="$nodeType" />
</xsl:attribute>
<!--merge the props from each part of a something node-->
<xsl:for-each select="$subTree"> <!-- access variable -->
<xsl:value-of select="prop[@type=$nodeType]"/>
<xsl:if test="not(prop[@type=$nodeType])">?</xsl:if>
</xsl:for-each>
</props>
</xsl:for-each>
</xsl:copy>
</xsl:template>