例如,让我们假定输入XML有以下结构:
<root>
<a>
<aa>1</aa>
<ab>2</ab>
<ac>3</ac>
</a>
<b>
<ba>4</ba>
<bb>5</bb>
<b>
<c>
<ca>
<caa>6</caa>
<cab>7</cab>
</ca>
</c>
</root>
鉴于组XPath的通过过滤元素:
/root/a/ab,
/root/a/ac,
/root/c/ca/cab
生成的XML应该是:
<root>
<a>
<ab>2</ab>
<ac>3</ac>
</a>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>
如何这可以通过XSLT来表达?
先感谢您
在XSLT 1.0实现这一点(与EXSLT可能一些小的帮助)或2.0,你可以通过破坏每个给定的路径为自己和祖先路径,使启动:
/root/c/ca/cab
例如,变为:
<path>/root/c/ca/cab</path>
<path>/root/c/ca</path>
<path>/root/c</path>
<path>/root</path>
这应该不是太困难的一个名为递归模板来完成。
一旦你在的地方,你可以使用身份变换加修饰的“直通”参数使每个处理单元可以计算路径本身,比较它的路径给出的列表,并确定它是否应该加入结果树与否。
在下面的样式表中,第1步中已跳过,结果被用作如果给出。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="paths">
<path>/root/a/ab</path>
<path>/root/a</path>
<path>/root</path>
<path>/root/a/ac</path>
<path>/root/a</path>
<path>/root</path>
<path>/root/c/ca/cab</path>
<path>/root/c/ca</path>
<path>/root/c</path>
<path>/root</path>
</xsl:param>
<xsl:template match="@* | node()">
<xsl:param name="pathtrain" />
<xsl:variable name="path" select="concat($pathtrain, '/', name())" />
<xsl:if test="$path=exsl:node-set($paths)/path or not(self::*)">
<xsl:copy>
<xsl:apply-templates select="@* | node()">
<xsl:with-param name="pathtrain" select="$path"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
适用于你的(校正)输入:
<root>
<a>
<aa>1</aa>
<ab>2</ab>
<ac>3</ac>
</a>
<b>
<ba>4</ba>
<bb>5</bb>
</b>
<c>
<ca>
<caa>6</caa>
<cab>7</cab>
</ca>
</c>
</root>
获得以下结果:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>
<ab>2</ab>
<ac>3</ac>
</a>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>
编辑:
请注意,使用上述基于字符串的测试时,重复的分支可能产生误报。 例如,适用于以下输入时:
<root>
<a>
<aa>1</aa>
<ab>2</ab>
<ac>3</ac>
</a>
<b>
<ba>4</ba>
<bb>5</bb>
</b>
<c>
<ca>
<caa>6</caa>
</ca>
</c>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>
上述样式表将产生:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>
<ab>2</ab>
<ac>3</ac>
</a>
<c>
<ca/>
</c>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>
如果这是一个问题,我会发布另一个(更复杂)XSLT 1.0的答案,通过测试唯一的ID,而不是消除了问题。
下面是使用撒克逊9.5 PE或EE和XSLT 3.0(工作草案版本目前在这些撒克逊版本中实现)的例子:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:param name="paths" as="xs:string">
/root/a/ab,
/root/a/ac,
/root/c/ca/cab
</xsl:param>
<xsl:variable name="nodes" as="node()*">
<xsl:evaluate xpath="$paths" context-item="/"/>
</xsl:variable>
<xsl:output indent="yes"/>
<xsl:template match="*[(.//node(), .//@*) intersect $nodes]">
<xsl:copy>
<xsl:apply-templates select="@* | node()[(., .//node(), .//@*) intersect $nodes]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()[. intersect $nodes]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
这里是一个不同的版本,其利用新的XSLT 3.0特性为具有可变参考作为匹配图案的,我认为这种方式,代码是更有效(与读取):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:param name="paths" as="xs:string">
/root/a/ab,
/root/a/ac,
/root/c/ca/cab
</xsl:param>
<xsl:variable name="nodes" as="node()*">
<xsl:evaluate xpath="$paths" context-item="/"/>
</xsl:variable>
<xsl:variable name="ancestors" as="node()*" select="$nodes/ancestor::node()"/>
<xsl:output indent="yes"/>
<xsl:template match="$ancestors">
<xsl:copy>
<xsl:apply-templates select="@* , node()[. intersect $ancestors or . intersect $nodes]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="$nodes">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
这是一个更复杂的XSLT 1.0答案(还需要EXSLT节点集()函数),通过执行变换的三道次重复解决分支的问题:
在第一阶段,给定的元素的ID被收集,使用恒等变换模板与“直通”参数,以确定他们 - 类似我以前的答案;
在第二遍中,每一个给定元素“收集”的本身及其祖先的ID;
在第三和最终道次,一个恒等变换模板再次使用去在整个源树和仅输出元件,其IDS已收集在步骤2中。
需要注意的是特定的路径并不需要在此版本进行预处理。
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="paths">
<path>/root/a/ab</path>
<path>/root/a/ac</path>
<path>/root/c/ca/cab</path>
</xsl:param>
<!-- first pass: get ids of given nodes -->
<xsl:variable name="ids">
<xsl:apply-templates select="/" mode="getids"/>
</xsl:variable>
<xsl:template match="*" mode="getids">
<xsl:param name="pathtrain" />
<xsl:variable name="path" select="concat($pathtrain, '/', name())" />
<xsl:if test="$path=exsl:node-set($paths)/path">
<id><xsl:value-of select="generate-id()" /></id>
</xsl:if>
<xsl:apply-templates select="*" mode="getids">
<xsl:with-param name="pathtrain" select="$path"/>
</xsl:apply-templates>
</xsl:template>
<!-- second pass: extend the list of ids to given nodes and their ancestors-->
<xsl:variable name="extids">
<xsl:for-each select="//*[generate-id(.)=exsl:node-set($ids)/id]">
<xsl:for-each select="ancestor-or-self::*">
<id><xsl:value-of select="generate-id()" /></id>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<!-- third pass: output the nodes whose ids are in the extended list -->
<xsl:template match="@* | node()">
<xsl:if test="generate-id(.)=exsl:node-set($extids)/id or not(self::*)">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
上述样式表,当施加到下面的“重复的分支”输入:
<root>
<a>
<aa>1</aa>
<ab>2</ab>
<ac>3</ac>
</a>
<b>
<ba>4</ba>
<bb>5</bb>
</b>
<c>
<ca>
<caa>6</caa>
</ca>
</c>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>
产生以下结果:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>
<ab>2</ab>
<ac>3</ac>
</a>
<c>
<ca>
<cab>7</cab>
</ca>
</c>
</root>