учитывая этот xml:
<root>
<list>
<!-- foo's comment -->
<item name="foo" />
<item name="bar" />
<!-- another foo's comment -->
<item name="another foo" />
</list>
</root>
Я хотел бы использовать XPath для выбора всех узлов объекта, которые имеют комментарий, сразу предшествующий им, который является, мне нравится выбирать "нечто" и "другое нечто" объекты, но не объект "панели".
Я уже бездельничал ось предыдущего одноуровневого элемента и комментарий () функция, но напрасно.
Кажется, это работает:
//comment()/following-sibling::*[1]/self::item
Он ищет непосредственно следующие за комментариями братья и сестры, которые также являются
элементами. Я не знаю лучшего способа выразить часть ::*[1]/self::item
, которая является уродливой; обратите внимание, что если бы было написано ::item[1]
, то он также находил бы
ы, не немедленно следующие за комментарием.
Текущее выбранное решение:
//comment()/following-sibling::*[1]/self::item
не работает в случае, когда между комментарием и элементом находится инструкция обработки (или целая группа инструкций обработки) - как было замечено в комментарии Мартина Хоннена.
В приведенном ниже решении такой проблемы нет.
Следующее выражение 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"/>
Как упоминалось в этой теме, введение теста (
) типа:
preceding-sibling::comment()
будет проверять только, есть ли у узла предшествующий брат или сестра, который является комментарием.
Если вы хотите узнать, из предшествующих братьев и сестер, которые являются элементами или комментариями, является ли ближайший из них комментарием, вы можете попробовать:
(preceding-sibling::*|preceding-sibling::comment())[1][self::comment()] # WRONG
НО: это не сработает, потому что хотя "
[1]
" означает первый в обратном направлении дляпредшествующий-родной
, это не значит, что для выражения с круглыми скобками - оно означает первый в порядке документа
Вы можете попробовать:
(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
: результирующий преобразованный файлРедактирование: поскольку он не учитывает инструкции по обработке, я оставил этот ответ как Community Wiki.