Перечисление или подсчет путей от текущего узла к каждому конечному узлу, у которого есть какое-либо свойство - PullRequest
2 голосов
/ 05 апреля 2011

Здесь каким-то образом используется XSLT для перечисления или подсчета путей от текущего узла к каждому листовому узлу на основе некоторых критериев.например, в конкретном случае здесь предположим, что текущим узлом является "t" и пути от текущего узла к каждому конечному узлу, которые не имеют атрибута "trg".Ниже, например,

<root>
  <t>
    <a1>
     <b1 trg="rr">
       <c1></c1>
     </b1>
     <b2>
       <c2></c2>
     </b2>
    </a1>
    <a2>
      <b3>
        <c3></c3>
      </b3>
    </a2>
  </t>
</root>

, здесь только путь с этим свойством t / a1 / b2 / c2 и t / a2 / b3 / c3

, но не т / a1 / b1 / c1

Ответы [ 4 ]

2 голосов
/ 05 апреля 2011

Самый простой подход:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="text()"/>
    <xsl:template match="text()" mode="search"/>
    <xsl:template match="t">
        <xsl:apply-templates mode="search">
            <xsl:with-param name="pPath" select="name()"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*" mode="search">
        <xsl:param name="pPath"/>
        <xsl:apply-templates mode="search">
            <xsl:with-param name="pPath" select="concat($pPath,'/',name())"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="*[not(*)]" mode="search">
        <xsl:param name="pPath"/>
        <xsl:value-of select="concat($pPath,'/',name(),'&#xA;')"/>
    </xsl:template>
    <xsl:template match="*[@trg]" mode="search" priority="1"/>
</xsl:stylesheet>

Выход:

t/a1/b2/c2
t/a2/b3/c3

Примечание : стиль полного натяжения. Правило начала поиска пути (t patttern). Правило для параметра туннелирования (* шаблон). Правило вывода пути листьев (шаблон *[not(*)]). Правило для условия прерывания рекурсии (шаблон *[@trg]).

0 голосов
/ 05 апреля 2011

Эту проблему лучше всего решить с помощью XSLT 2.0 / XPath 2.0

Очень первоначальная попытка XSLT 1.0 :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="*[not(*)]">
   <xsl:call-template name="buildPath">
    <xsl:with-param name="pstartNode" select="/*/t"/>
   </xsl:call-template>
 </xsl:template>

 <xsl:template match="text()"/>

 <xsl:template name="buildPath">
  <xsl:param name="pstartNode" select="."/>
  <xsl:param name="pendNode" select="."/>

  <xsl:choose>
   <xsl:when test=
    "not(count($pstartNode | $pendNode/ancestor-or-self::node())
        =
         count($pendNode/ancestor-or-self::node())
         )
    ">
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vPath" select=
      "ancestor::*
          [count(.|$pstartNode/descendant-or-self::*)
          =
           count($pstartNode/descendant-or-self::*)
          ]
      "/>
     <xsl:if test="not($vPath[@trg])">
      <xsl:for-each select=
        "ancestor::*
          [count(.|$pstartNode/descendant-or-self::*)
          =
           count($pstartNode/descendant-or-self::*)
          ]">
        <xsl:value-of select="concat(name(),'/')"/>
      </xsl:for-each>
      <xsl:value-of select="concat(name(), '&#xA;')"/>
     </xsl:if>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<root>
    <t>
        <a1>
            <b1 trg="rr">
                <c1></c1>
            </b1>
            <b2>
                <c2></c2>
            </b2>
        </a1>
        <a2>
            <b3>
                <c3></c3>
            </b3>
        </a2>
    </t>
</root>

дает желаемый, правильный результат :

t/a1/b2/c2
t/a2/b3/c3
0 голосов
/ 05 апреля 2011

Вот таблица стилей:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <paths>
            <xsl:apply-templates select="root/t">
                <xsl:with-param name="path"/>
            </xsl:apply-templates>
        </paths>
    </xsl:template>

    <xsl:template name="path">
        <xsl:param name="path"/>
        <xsl:choose>
            <xsl:when test="string-length($path)">
                <xsl:value-of select="$path"/>/<xsl:value-of select="name()"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="name()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="*">
        <xsl:param name="path"/>
        <xsl:if test="not(@trg)">
            <xsl:if test="not(count(*))">
                <path>
                    <xsl:call-template name="path">
                        <xsl:with-param name="path">
                            <xsl:value-of select="$path"/>
                        </xsl:with-param>
                    </xsl:call-template>
                </path>
            </xsl:if>
            <xsl:apply-templates select="*">
                <xsl:with-param name="path">
                    <xsl:call-template name="path">
                        <xsl:with-param name="path">
                            <xsl:value-of select="$path"/>
                        </xsl:with-param>
                    </xsl:call-template>
                </xsl:with-param>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

Идея очень проста - основной шаблон обработки (math="*") является рекурсивной функцией, применимой к каждому узлу.У него есть один параметр - path, который является фактическим путем к этому узлу из корневого узла поиска (t).Если у узла нет дочерних элементов, он может быть выведен.

Другой шаблон с именем path представляет собой простую вспомогательную функцию для правильного создания пути.Если бы вы могли зарегистрировать функцию XPath в вашем процессоре XSLT, я бы предпочел сделать это и избавиться от многословных call-template конструкций.Весь вызов call-template ... может быть простым <xsl:value-of select="myfn:path($path)"/>, который значительно повысит читабельность, и это очень важно для XSLT;)

В любом случае, я чувствую, что слишком много path слова в моем объяснении :), поэтому не стесняйтесь спрашивать, если что-то не ясно.

0 голосов
/ 05 апреля 2011

Я могу придумать, как этого добиться, но это не выглядит красиво, и, надеюсь, кто-то придумает лучший способ ...

Во-первых, я определил переменную с уровнем текущего узла

<xsl:variable name="level">
   <xsl:value-of select="count(ancestor::*)"/>
</xsl:variable>

Далее, сопоставьте все потомки или текущий узел, которые оказались конечными узлами и не имеют никаких предков (включая самого себя, но только до текущего уровня) с атрибутом @trg = 'rr' как указано.

<xsl:apply-templates select=".//*[not(node())][not(ancestor-or-self::*[count(ancestor::*) &gt; $level][@trg='rr'])]">
   <xsl:with-param name="currentLevel" select="$level"/>
</xsl:apply-templates>

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

Вот полная таблица стилей

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:template match="/">
      <xsl:apply-templates select="root/t"/>
   </xsl:template>

   <xsl:template match="t">

      <!-- The level of the current node -->
      <xsl:variable name="level">
         <xsl:value-of select="count(ancestor::*)"/>
      </xsl:variable>

      <paths>
         <!-- Match all leaf nodes where there is not an ancestor (up to the current node) with the attribute @trg = 'rr' -->
         <xsl:apply-templates select=".//*[not(node())][not(ancestor-or-self::*[count(ancestor::*) &gt; $level][@trg=&apos;rr&apos;])]">
            <xsl:with-param name="currentLevel" select="$level"/>
         </xsl:apply-templates>
      </paths>
   </xsl:template>

   <!-- For leaf nodes, write out the ancestor path to a specified level -->
   <xsl:template match="*[not(node())]">
      <xsl:param name="currentLevel"/>
      <path>
         <xsl:for-each select="ancestor::*[count(ancestor::*) &gt;= $currentLevel]">
            <xsl:value-of select="name()"/>
            <xsl:text>/</xsl:text>
         </xsl:for-each>
         <xsl:value-of select="name()"/>
      </path>
   </xsl:template>

</xsl:stylesheet>

При применении к данному XML возвращаются следующие результаты

<paths>
   <path>t/a1/b2/c2</path>
   <path>t/a2/b3/c3</path>
</paths>
...