xslt и xpath: соответствуют предыдущим комментариям - PullRequest
7 голосов
/ 10 апреля 2010

с учетом этого xml:

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

Я бы хотел использовать XPath для выбора всех узлов-элементов, у которых непосредственно перед ними стоит комментарий, то есть мне нравится выбирать элементы "foo" и "another foo", но не элемент "bar".

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

Ответы [ 3 ]

3 голосов
/ 10 апреля 2010

Это похоже на работу:

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

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

3 голосов
/ 10 апреля 2010

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

//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"/>
0 голосов
/ 10 апреля 2010

Как уже упоминалось в этой теме , вводится тест (<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: полученный преобразованный файл

Редактировать: поскольку он не учитывает инструкции по обработке, я оставил этот ответ в виде вики сообщества.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...