pto*_*ato 5 xslt xpath xpath-1.0 xslt-1.0
我有这样的XML:
<span>1</span>
<span class="x">2</span>
<span class="x y">3</span>
<span class="x">4</span>
<span>5</span>
<span class="x">6</span>
<span>7</span>
<span class="x">8</span>
Run Code Online (Sandbox Code Playgroud)
我想要的是使用XSLT样式表将class属性包含的所有元素的内容x放入一个<x>元素中.所以输出应该是这样的:
1 <x>234</x> 5 <x>6</x> 7 <x>8</x>
Run Code Online (Sandbox Code Playgroud)
(或者,理想情况下,
1 <x>2<y>3</y>4</x> 5 <x>6</x> 7 <x>8</x>
Run Code Online (Sandbox Code Playgroud)
但是当我解决这个问题时,这是一个需要解决的问题.)
这是我的XSLT的相关片段:
<xsl:template match="span[contains(@class,'x') and preceding-sibling::span[1][not(contains(@class,'x'))]]">
<x><xsl:for-each select=". | following-sibling::span[contains(@class,'x')]">
<xsl:value-of select="text()"/>
</xsl:for-each></x>
</xsl:template>
<xsl:template match="span[contains(@class,'x') and preceding-sibling::span[1][contains(@class,'x')]]">
</xsl:template>
<xsl:template match="span">
<xsl:value-of select="text()"/>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
这产生的是:
1 <x>23468</x> 5 <x>68</x> 7 <x>8</x>
Run Code Online (Sandbox Code Playgroud)
我很确定我必须在XPath表达式中使用一个计数,这样它就不会选择带有类x的所有以下元素,只是连续的.但是我如何计算连续的?或者我这样做是错误的吗?
这很棘手,但可行(长时间预读,对不起).
根据XPath轴(根据定义,它不是连续的),"连续性"的关键是检查"首先满足条件" 的相反方向上的最近节点是否也是"开始"手头系列的节点:
a b <- first node to fulfill the condition, starts series 1 b <- series 1 b <- series 1 a b <- first node to fulfill the condition, starts series 2 b <- series 2 b <- series 2 a
在你的情况下,系列包括<span>有串节点x在他们@class:
span[contains(concat(' ', @class, ' '),' x ')]
Run Code Online (Sandbox Code Playgroud)
请注意,我将空格连接起来以避免误报.
甲<span>启动一系列(即一个"先满足条件")可以被定义为一个有一个x在它的类,而不是直接由另一个之前<span>还具有一个x:
not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])
Run Code Online (Sandbox Code Playgroud)
我们必须检查这个条件,<xsl:if>以避免模板为一系列节点生成输出(即模板只对"起始节点"执行实际工作).
现在到了棘手的部分.
从这些"起始节点"中的每一个,我们必须选择在其类following-sibling::span中具有a的所有节点x.还包括当前span要考虑只有一个元素的系列.好的,很简单:
. | following-sibling::span[contains(concat(' ', @class, ' '),' x ')]
Run Code Online (Sandbox Code Playgroud)
对于这些中的每一个,我们现在发现它们最接近的"起始节点"是否与模板正在处理的那个相同(即,它们开始了它们的系列).这意味着:
他们必须是一系列的一部分(即它们必须遵循span与x)
preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')]
Run Code Online (Sandbox Code Playgroud)现在删除任何span其起始节点与当前系列起始器不同的任何节点.这意味着我们检查任何前面的兄弟span(有一个x),它本身不是直接前面span带有x:
preceding-sibling::span[contains(concat(' ', @class, ' '),' x ')][
not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])
][1]
Run Code Online (Sandbox Code Playgroud)然后我们generate-id()用来检查节点身份.如果找到的节点相同$starter,则当前跨度是属于连续系列的跨度.
把它们放在一起:
<xsl:template match="span[contains(concat(' ', @class, ' '),' x ')]">
<xsl:if test="not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])">
<xsl:variable name="starter" select="." />
<x>
<xsl:for-each select="
. | following-sibling::span[contains(concat(' ', @class, ' '),' x ')][
preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')]
and
generate-id($starter)
=
generate-id(
preceding-sibling::span[contains(concat(' ', @class, ' '),' x ')][
not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])
][1]
)
]
">
<xsl:value-of select="text()" />
</xsl:for-each>
</x>
</xsl:if>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
是的,我知道它不漂亮.<xsl:key>Dimitre的答案显示,有一种更有效的基础解决方案.
使用示例输入,将生成以下输出:
1
<x>234</x>
5
<x>6</x>
7
<x>8</x>
Run Code Online (Sandbox Code Playgroud)
I. XSLT解决方案:
我想要的是使用XSLT样式表将其class属性包含的所有元素的内容
x放入一个<x>元素中.所以输出应该是这样的:Run Code Online (Sandbox Code Playgroud)1 <x>234</x> 5 <x>6</x> 7 <x>8</x>
这种转变:
<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:key name="kFollowing" match=
"span[contains(concat(' ', @class, ' '),
' x ')
]"
use="generate-id(preceding-sibling::span
[not(contains(concat(' ', @class, ' '),
' x '))
][1]
)
"/>
<xsl:template match=
"span[contains(concat(' ', @class, ' '), ' x ')
and
not(contains(concat(' ', preceding-sibling::span[1]/@class, ' '),
' x '
)
)
]"
>
<x>
<xsl:apply-templates mode="inGroup" select=
"key('kFollowing',
generate-id(preceding-sibling::span [not(contains(concat(' ', @class, ' '), ' x ')
)
][1]
)
)
"/>
</x>
</xsl:template>
<xsl:template match=
"span[contains(concat(' ', @class, ' '), ' x ')
and
contains(concat(' ', preceding-sibling::span[1]/@class, ' '),
' x '
)
]
"/>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
当应用于提供的XML文档时(包装成单个顶部元素html以使其格式良好):
<html>
<span>1</span>
<span class="x">2</span>
<span class="x y">3</span>
<span class="x">4</span>
<span>5</span>
<span class="x">6</span>
<span>7</span>
<span class="x">8</span>
</html>
Run Code Online (Sandbox Code Playgroud)
产生想要的,正确的结果:
1<x>234</x>5<x>6</x>7<x>8</x>
Run Code Online (Sandbox Code Playgroud)
然后"理想"补充:
或者,理想情况下,
Run Code Online (Sandbox Code Playgroud)1 <x>2<y>3</y>4</x> 5 <x>6</x> 7 <x>8</x>但是当我解决这个问题时,这是一个需要解决的问题.)
只需在上面的解决方案中添加此模板:
<xsl:template mode="inGroup" match=
"span[contains(concat(' ', @class, ' '),
' y '
)
]">
<y><xsl:value-of select="."/></y>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
将如此修改的解决方案应用于同一XML文档时,再次生成(新)想要的结果:
1<x>2<y>3</y>4</x>5<x>6</x>7<x>8</x>
Run Code Online (Sandbox Code Playgroud)
II.XSLT 2.0解决方案:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="my xs"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:for-each-group select="span" group-adjacent=
"contains(concat(' ',@class,' '), ' x ')">
<xsl:sequence select=
"if(current-grouping-key())
then
my:formatGroup(current-group())
else
data(current-group())
"/>
</xsl:for-each-group>
</xsl:template>
<xsl:function name="my:formatGroup" as="node()*">
<xsl:param name="pGroup" as="node()*"/>
<x>
<xsl:apply-templates select="$pGroup"/>
</x>
</xsl:function>
<xsl:template match=
"span[contains(concat(' ',@class, ' '), ' y ')]">
<y><xsl:apply-templates/></y>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
当在同一XML文档(上面)上应用此XSLT 2.0转换时,会生成所需的"理想"结果:
1<x>2<y>3</y>4</x>5<x>6</x>7<x>8</x>
Run Code Online (Sandbox Code Playgroud)