XSLT - выбрать узлы, которые пришли после другого узла - PullRequest
6 голосов
/ 13 июня 2011

Я пытаюсь выбрать все узлы, которые 1) идут после узла с определенным свойством и 2) сами имеют определенное свойство.Поэтому, если бы у меня был следующий XML:

<node id="1"><child attr="valueOfInterest"/></node>
<node id="2"><child attr="boringValue"/></node>
...
<node id="3"><child attr="valueOfInterest"/></node>
<node id="4"><child attr="boringValue"/></node>
<node id="5"><child attr="boringValue"/></node>
<node id="6"><child attr="boringValue"/></node>
...

Мой XSLT проходит через каждый тег node.На каждом node я хочу, чтобы он выбирал все предыдущие node с, которые произошли с самого последнего node с child, attr которого был valueOfInterest.Поэтому, если бы я был на узле № 2, я бы хотел установить пустой узел.Если бы я был на узле № 6, я бы хотел выбрать узлы № 4 и 5. В настоящее время у меня есть следующий XSLT:

<xsl:variable name="prev_vals"
    select="preceding-sibling::node/child[@attr = $someValueICareAbout]/@attr"/>

Так что этот XSLT получает все предшествующие attr значения, которые являютсяконкретное значение.Как получить только те предшествующие значения attr, которые находятся в node с, которые следуют после самого последнего node, у которого child имеет конкретное значение attr (т. Е. "ValueOfInterest")?Атрибут id в тегах node не обязательно будет увеличиваться, поэтому мы не можем сравнивать его с этим.

Редактировать: Я думал, что они могут быть полезны:

<xsl:variable name="prev_children_of_interest"
    select="preceding-sibling::node/child[@attr != $someValueICareAbout]"/>
<xsl:variable name="mru_child_of_interest"
    select="$prev_children_of_interest[count($prev_children_of_interest)]"/>

Итак, это все предыдущие теги child с attr=valueOfInterest, а затем самый последний использованный (ближайший к текущему узлу) тег child, имеющий атрибут, который я ищу.Из mru_child_of_interest мы можем найти самый последний использованный тег parent, но как нам тогда искать узлы, которые идут после этого тега?

Ответы [ 4 ]

6 голосов
/ 14 июня 2011

Я не уверен, правильно ли я понимаю ваш вопрос, но вот некоторые XSL 1.0 (дополнительные each-nodes атрибуты только для информации):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="nodes">
        <xsl:copy>
            <xsl:apply-templates select="node"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="node">
        <xsl:variable name="someValueICareAbout">valueOfInterest</xsl:variable>

        <xsl:variable name="recentParticularNode"
            select="preceding-sibling::node[child/@attr = $someValueICareAbout][1]"/>

        <xsl:variable name="recentParticularNodePosition"
            select="count($recentParticularNode/preceding-sibling::node) + 1"/>

        <xsl:variable name="currentPosition" select="position()"/>

        <xsl:if test="child/@attr != $someValueICareAbout">
            <each-nodes id="{@id}" cp="{$currentPosition}" 
                    rpnp="{$recentParticularNodePosition}">
                <xsl:copy-of
                    select="../node[position() &gt; $recentParticularNodePosition
                    and position() &lt; $currentPosition]"/>
            </each-nodes>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Входной XML:

<?xml version="1.0" encoding="UTF-8"?>
<nodes>
    <node id="1"><child attr="valueOfInterest"/></node>
    <node id="2"><child attr="boringValue2"/></node>
    <node id="3"><child attr="valueOfInterest"/></node>
    <node id="4"><child attr="boringValue4"/></node>
    <node id="5"><child attr="boringValue5"/></node>
    <node id="6"><child attr="boringValue6"/></node>
</nodes>

Результат XML:

<?xml version="1.0" encoding="UTF-8"?>
<nodes>
   <each-nodes id="2" cp="2" rpnp="1"/>
   <each-nodes id="4" cp="4" rpnp="3"/>
   <each-nodes id="5" cp="5" rpnp="3">
      <node id="4">
         <child attr="boringValue4"/>
      </node>
   </each-nodes>
   <each-nodes id="6" cp="6" rpnp="3">
      <node id="4">
         <child attr="boringValue4"/>
      </node>
      <node id="5">
         <child attr="boringValue5"/>
      </node>
   </each-nodes>
</nodes>
3 голосов
/ 14 июня 2011

Это преобразование копирует в точности нужные узлы :

<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:template match=
 "node[not(child/attr='valueOfInterest')]">

 <xsl:variable name="vFollowing" select=
 "preceding-sibling::node
          [child/@attr='valueOfInterest'][1]
            /following-sibling::node"/>

 <xsl:variable name="vPreceding" select=
  "preceding-sibling::node"/>

  <xsl:copy-of select=
  "$vFollowing[count(. | $vPreceding)
              =
               count($vPreceding)
              ]
  "/>
======================
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

при применении к этому XML-документу (на основе предоставленного XML-фрагмента и оборачивания его в верхний элемент, чтобы сделать его правильно сформированным XML-документом):

<t>
    <node id="1">
        <child attr="valueOfInterest"/>
    </node>
    <node id="2">
        <child attr="boringValue"/>
    </node>...
    <node id="3">
        <child attr="valueOfInterest"/>
    </node>
    <node id="4">
        <child attr="boringValue"/>
    </node>
    <node id="5">
        <child attr="boringValue"/>
    </node>
    <node id="6">
        <child attr="boringValue"/>
    </node>...
</t>

желаемый, правильный результат получается :

======================

======================
 <node id="2">
   <child attr="boringValue"/>
</node>
======================

======================
 <node id="4">
   <child attr="boringValue"/>
</node>
======================
 <node id="4">
   <child attr="boringValue"/>
</node>
<node id="5">
   <child attr="boringValue"/>
</node>
======================

Объяснение

  1. Здесь мы используем известную формулу Kayessian (обнаруженную пользователем SO @Mayhael Kay) для пересечения двух наборов узлов $ns1 и $ns2:

    ns1 [count (. | $ Ns2) = count ($ ns2)]

  2. Мы просто заменим $vFollowing и $vPreceding на $ns1 и $ns2 в приведенной выше формуле.

$vFollowing is defined to contain exactly all the following sibling elements named узел of the nearest узел`, который удовлетворяет условию (будет интересно).

$vPreceding - это набор всех node элементов, которые предшествуют братьям и сестрам текущего (согласованного) узла.

0,3. Их пересечение - это именно то, что нужно для набора узлов.

3 голосов
/ 14 июня 2011

Похоже, вы хотите пересечение двух множеств.Набор 1 - это все узлы после последнего valueOfInterest.Набор 2 - это все узлы перед текущим узлом, которые не содержат valueOfInterest.Для XPath 1.0 следующее даст вам пересечение (ссылка найдена здесь ).

$set1[count($set2|.)=count($set2)]

Учитывая ваш ввод, следующий XSL демонстрирует искомый набор узлов

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:variable name="someValueICareAbout">valueOfInterest</xsl:variable>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node[child/@attr='boringValue']">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>

      <xsl:variable name="set1" select="preceding-sibling::node[child/@attr='valueOfInterest'][1]/following-sibling::node "/>

      <xsl:variable name="set2" select="preceding-sibling::node[child/@attr='boringValue']"/>

      <predecessors>
          <xsl:copy-of select="$set1[count($set2|.)=count($set2)]"/>
      </predecessors>

    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Вот вывод

<xml>
  <node id="1">
    <child attr="valueOfInterest"/>
  </node>
  <node id="2">
    <child attr="boringValue"/>
    <predecessors/>
  </node>
  <node id="3">
    <child attr="valueOfInterest"/>
  </node>
  <node id="4">
    <child attr="boringValue"/>
    <predecessors/>
  </node>
  <node id="5">
    <child attr="boringValue"/>
    <predecessors>
      <node id="4">
        <child attr="boringValue"/>
      </node>
    </predecessors>
  </node>
  <node id="6">
    <child attr="boringValue"/>
    <predecessors>
      <node id="4">
        <child attr="boringValue"/>
      </node>
      <node id="5">
        <child attr="boringValue"/>
      </node>
    </predecessors>
  </node>
</xml>

Обратите внимание, причина, по которой я использую [1] в preceding-sibling::node[child/@attr='valueOfInterest'][1], заключается в том, что порядок набора узлов меняется на preceding-sibling см. Здесь1018 *.

Если у вас XPath 2.0, вы можете использовать оператор intersect

  <predecessors>
    <xsl:copy-of select="$set1 intersect $set2"/>
  </predecessors>

. Результат тот же.

2 голосов
/ 14 июня 2011

Вот один из способов сделать это в XSLT 2.0:

<xsl:variable name="prevVOI"
      select="(preceding-sibling::node[child/@attr = 'valueOfInterest'])[last()]" />
<xsl:variable name="prevNodesAfterVOI"
      select="preceding-sibling::node[. >> $prevVOI]" />
...