xslt и xpath: комментарии предшествования соответствия

учитывая этот xml:

<root>
    <list>
        <!-- foo's comment -->
        <item name="foo" />
        <item name="bar" />
        <!-- another foo's comment -->
        <item name="another foo" />
    </list>
</root>

Я хотел бы использовать XPath для выбора всех узлов объекта, которые имеют комментарий, сразу предшествующий им, который является, мне нравится выбирать "нечто" и "другое нечто" объекты, но не объект "панели".

Я уже бездельничал ось предыдущего одноуровневого элемента и комментарий () функция, но напрасно.

7
задан miasbeck 10 April 2010 в 11:15
поделиться

3 ответа

Кажется, это работает:

//comment()/following-sibling::*[1]/self::item

Он ищет непосредственно следующие за комментариями братья и сестры, которые также являются элементами. Я не знаю лучшего способа выразить часть ::*[1]/self::item, которая является уродливой; обратите внимание, что если бы было написано ::item[1], то он также находил бы ы, не немедленно следующие за комментарием.

3
ответ дан 7 December 2019 в 09:59
поделиться

Текущее выбранное решение:

//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"/>
3
ответ дан 7 December 2019 в 09:59
поделиться

Как упоминалось в этой теме, введение теста () типа:

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.

0
ответ дан 7 December 2019 в 09:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: