XSLT和XPath:匹配前面的评论
给出这个XML:XSLT和XPath:匹配前面的评论
<root>
<list>
<!-- foo's comment -->
<item name="foo" />
<item name="bar" />
<!-- another foo's comment -->
<item name="another foo" />
</list>
</root>
我想使用XPath来选择所有项目节点有评论紧接他们,这是我喜欢选择“富”和“另一富”项目,但不是“酒吧”项目。
我已经弄乱了前面的兄弟轴和comment()函数,但无济于事。
这似乎工作:
//comment()/following-sibling::*[1]/self::item
它寻找紧随其后的这也是<item>
元素评论兄弟姐妹。我不知道一个更好的方式来表达::*[1]/self::item
部分,这很丑陋;注意,如果它被写入::item[1]
那么它也会发现<item>
s不是立即通过评论继续。
如果在注释和'item'元素之间有处理指令会发生什么?当然,一个边缘案例,但原始海报首先需要澄清一个'item'元素之前是否有注释前面的处理指令是他想要选择的元素。 – 2010-04-10 12:35:59
@凯文:谢谢凯文。这个X-Path足够简单,让我能够掌握,而且它的工作原理非常完美:) @Martin:幸运的是,我是Xml输入的主人,所以我确信不会有任何处理指令。无论如何感谢有用的提示。 – miasbeck 2010-04-10 13:52:14
Martin Honnen的评论是正确的。 – 2010-04-10 16:09:39
如this thread所提到的,将测试(<xsl:if test="..."></xsl:if>
)像:
前同辈::评论()
那只测试节点是否具有在前兄弟这是一个注释。
如果你想知道,前面的兄弟姐妹是元素或意见,最近的一个是评论,你可以尝试:
(preceding-sibling::*|preceding-sibling::comment())[1][self::comment()] # WRONG
但是:这是行不通的,因为虽然“
[1]
”在向后方向上的第一装置用于preceding-sibling
,这并不意味着,对于一个括号表达式 - 它 第一装置,按文档顺序
你可以试试:
(preceding-sibling::*|preceding-sibling::comment())[last()][self::comment()]
或
preceding-sibling::node()[self::*|self::comment()][1][self::comment()]
例如:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="no" indent="no"/>
<xsl:template match="//item">
<xsl:if test="preceding-sibling::node()[self::*|self::comment()][1][self::comment()]">
<xsl:value-of select="./@name" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
只会显示:
foo
another foo
打字时:
C:\Prog\xslt\preceding-sibling_comment>
java -cp ..\saxonhe9-2-0-6j\saxon9he.jar net.sf.saxon.Transform -s:test.xml -xsl:t.xslt -o:res.xml
有:
-
test.xml
:您的文件显示在你的问题 -
t.xslt
:XSLT文件上面 -
res.xml
:得到的变换文件
编辑:因为它没有考虑到处理指令,所以我把这个答案留作社区Wiki。
当前所选的溶液:
//comment()/following-sibling::*[1]/self::item
不会在存在一个处理。第指令(或整组处理指令)的注释和元件之间的情况下工作 - 正如Martin Honnen在评论中注意到的那样。
下面的解决方案不存在这样的问题。
下面的XPath表达式选择,它们或者立即由注释节点之前,或者是紧接在空白仅文本节点,该节点立即被注释节点之前先只元素节点:
(//comment()
/following-sibling::node()
[1]
[self::text()
and
not(normalize-space())
]
/following-sibling::node()
[1] [self::item]
)
|
(//comment()
/following-sibling::node()
[1]
[self::item]
)
这是一个完整的测试:
我们用这个XML文档:
<root>
<list>
<!-- foo's comment -->
<item name="foo" />
<item name="bar" />
<!-- another foo's comment -->
<item name="another foo" />
<!-- comment 3 --><item name="immed.after 3"/>
<!-- comment 4 --><?PI ?><item name="after PI"/>
</list>
</root>
当以下变换被施加在上面的XML文档:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"
(//comment()
/following-sibling::node()
[1]
[self::text()
and
not(normalize-space())
]
/following-sibling::node()
[1] [self::item]
)
|
(//comment()
/following-sibling::node()
[1]
[self::item]
)
"/>
</xsl:template>
</xsl:stylesheet>
有用,正确的结果产生:
<item name="foo"/>
<item name="another foo"/>
<item name="immed.after 3"/>
好,完整的答案。 +1 – VonC 2010-04-10 16:04:44
注意,在XSLT/XPath数据模型的那些项目元素你正在寻找一个不是立即在注释节点之前,而是在一个空白的文本节点之前。 因此,除非在样式表中使用'',否则这些空白文本节点可能会妨碍您的使用。 –
2010-04-10 12:32:07
好问题(+1)。请参阅我的答案,了解唯一(目前)完整的解决方案。 Martin Honnen的评论是正确的,尽管当前选定的答案可能适用于您,但它可能会突然停止这样做(如果XPath引擎提供了XML文档,其空白节点没有被剥离)。 – 2010-04-10 16:08:13