尝试超过8小时后,我希望有人可以帮助我:
给出一本书的以下(简化)XML:
<book>
<section name="A">
<chapter name="I">
<paragraph name="1"/>
<paragraph name="2"/>
</chapter>
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
<section name="B">
<chapter name="III"/>
<chapter name="IV"/>
</section>
</book>
我可以使用以下XSL基于给定参数提取书籍XML的任何部分(部分,章节或段落):
<xsl:param name="subSelectionXPath" required="yes" as="node()"/>
<xsl:template match="/">
<xsl:apply-templates select="$subSelectionXPath"/>
</xsl:template>
<xsl:template match="*">
<!-- output node with all children -->
<xsl:copy-of select="."/>
</xsl:template>
和参数 $ subSelectionXPath ,其值类似于
doc(filename)//chapter[@name='II']
产生输出:
<chapter name="II">
<paragraph name="1"/>
</chapter>
我想要实现的另一个目的是让祖先XML分支包含所选的XML片段,即:
<book>
<section name="A">
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
</book>
我想(并尝试)遍历XML树并测试当前节点是否是祖先,类似于(伪代码):
<xsl:if test="node() in $subSelectionXPath/ancestor::node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:if>
我也尝试过xsl:key,但恐怕我对XSLT的了解已经在这里结束了。有什么想法吗?
答案 0 :(得分:6)
从您的代码中可以明显看出您使用的是XSLT 2.0。
此XSLT 2.0转换:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="subSelectionXPath"
as="node()" select="//chapter[@name='II']"
/>
<xsl:template match="*[descendant::node() intersect $subSelectionXPath]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[. intersect $subSelectionXPath]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<book>
<section name="A">
<chapter name="I">
<paragraph name="1"/>
<paragraph name="2"/>
</chapter>
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
<section name="B">
<chapter name="III"/>
<chapter name="IV"/>
</section>
</book>
产生完全正确的结果:
<book>
<section name="A">
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
</book>
解释:我们只有两个模板:
匹配任何后代与$subSelectionXPath
节点集非空交集的元素的模板。在这里,我们“浅层复制”元素并将模板应用于其子元素。
匹配属于$subSelectionXPath
节点集的元素的模板。在这里,我们复制了以该元素为根的整个子树。
请注意使用XPath 2.0 intersect
运算符。
否显式递归。
<强> II。 XSLT 1.0解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="subSelectionXPath"
select="//chapter[@name='II']"
/>
<xsl:template match="*">
<xsl:choose>
<xsl:when test=
"descendant::node()
[count(.|$subSelectionXPath)
=
count($subSelectionXPath)
]
">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:when>
<xsl:when test=
"count(.|$subSelectionXPath)
=
count($subSelectionXPath)
">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
当此转换应用于同一XML文档(如上所示)时,会生成相同的想要和正确的结果:
<book>
<section name="A">
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
</book>
解释:这本质上是XSLT 2.0解决方案,其中XPath 2.0 intersect
运算符使用着名的Kayessian(针对@Michael Kay)公式转换为XPath 1.0两个节点集$ns1
和$ns2
:
$ns1[count(.|$ns2) = count($ns2)]
答案 1 :(得分:3)
我认为你要做的事情的方法是使用递归模板:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:call-template name="copyElementsOnAncestorAxis">
<xsl:with-param name="nodeList"
select="//chapter[@name='I']/ancestor-or-self::*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="copyElementsOnAncestorAxis">
<xsl:param name="nodeList"/>
<xsl:choose>
<!-- if the context node is the last node in the list, copy it entirely -->
<xsl:when test=". = $nodeList[count($nodeList)]">
<xsl:copy-of select="."/>
</xsl:when>
<!-- otherwise, just copy the element, its attributes, and any child element that
is also in the node list -->
<xsl:otherwise>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="*[. = $nodeList]">
<xsl:call-template name="copyElementsOnAncestorAxis">
<xsl:with-param name="nodeList"
select="$nodeList"/>
</xsl:call-template>
</xsl:for-each>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
,当应用于您提供的XML时,会生成以下内容:
<book>
<section name="A">
<chapter name="I">
<paragraph name="1" />
<paragraph name="2" />
</chapter>
</section>
</book>
答案 2 :(得分:2)
这是另一种基于递归的解决方案。说明:
这里的变换:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="subSelectionXPath"
select="document('test_input2.xml')//chapter[@name='II']"/>
<xsl:template match="/">
<xsl:apply-templates
select="$subSelectionXPath/ancestor::*[position()=last()]"/>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test="$subSelectionXPath/ancestor::*
[generate-id() = generate-id(current())]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:choose>
<xsl:when test="generate-id(.)=
generate-id($subSelectionXPath/ancestor::*[1])">
<xsl:copy-of select="$subSelectionXPath"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="*"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
当应用于问题中提供的输入时,假设输入参数值在问题中相同,则产生:
<book>
<section name="A">
<chapter name="II">
<paragraph name="1"/>
</chapter>
</section>
</book>