Как индексировать / подсчитывать узлы, содержащие подузел, в формате 1, 2, 3, 4 - PullRequest
2 голосов
/ 12 августа 2011

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

XML

<?xml version="1.0" encoding="UTF-8"?>
<entry>
      <node>
           <subnodeA>test_subnodeA1</subnodeA>
           <subnodeB>test_subnodeB1</subnodeB>
      </node>
      <node>
           <subnodeA>test_subnodeA2</subnodeA>
           <subnodeB>test_subnodeB2</subnodeB>
      </node>
      <node>
           <subnodeA>test_subnodeA3</subnodeA>
      </node>
      <node>
           <subnodeA>test_subnodeA4</subnodeA>
           <subnodeB>test_subnodeB3</subnodeB>
      </node>
 </entry>

XSLT

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

    <!-- Initial usage -->
    <xsl:for-each select="node">
        <xsl:value-of select="subnodeA"/>
             <xsl:if test="subnodeB != ''">
                 <xsl:value-of select="position()"/>  
             </xsl:if>
    </xsl:for-each>

    <!-- Second usage -->
    <xsl:for-each select="node">
        <xsl:if test="subnodeB != ''">
            <xsl:value-of select="position()"/>. 
            <xsl:value-of select="subnodeB"/>
         </xsl:if>
    </xsl:for-each>

</xsl:template>

Я хочу отобразить что-то вроде

test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 3

1 test_subnodeB1
2 test_subnodeB2
3 test_subnodeB3

Но, используя мой метод, я могу получить только

test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 4

1 test_subnodeB1
2 test_subnodeB2
4 test_subnodeB3

Некоторые записи имеют несколько узлов без подузла B, а затем узел с одним, поэтому мои списки начинаются с 3, 4 или 5.

Ответы [ 3 ]

1 голос
/ 12 августа 2011

В первом случае вы можете получить позицию, сосчитав предшествующих братьев и сестер

<xsl:value-of select="count(preceding-sibling::node[subnodeA and subnodeB]) + 1" />

Во втором случае вы просто хотите узел элементов с подузлом B

<xsl:for-each select="node[subnodeA and subnodeB]">

Вот пример XSLT. Обратите внимание, что я перешел от использования xsl: for-each к использованию xsl: apply-templates

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

   <xsl:template match="/entry">
      <xsl:apply-templates select="node" mode="initial" />
      <xsl:apply-templates select="node[subnodeB]" mode="second" />
   </xsl:template>

   <xsl:template match="node" mode="initial">
      <xsl:value-of select="subnodeA" />
      <xsl:if test="subnodeB != ''">
      <xsl:text> - </xsl:text><xsl:value-of select="count(preceding-sibling::node[subnodeA and subnodeB]) + 1" />
      </xsl:if>
      <xsl:text>&#13;</xsl:text>
   </xsl:template>

   <xsl:template match="node" mode="second">
      <xsl:value-of select="position()" /> - <xsl:value-of select="subnodeB" /> 
      <xsl:text>&#13;</xsl:text>
   </xsl:template>
</xsl:stylesheet>

Это генерирует следующий текстовый вывод

test_subnodeA1 - 1
test_subnodeA2 - 2
test_subnodeA3
test_subnodeA4 - 3
1 - test_subnodeB1
2 - test_subnodeB2
3 - test_subnodeB3
0 голосов
/ 12 августа 2011

Это одно из самых простых возможных решений - нет xsl:for-each, нет count(), нет специальных осей и вообще нет явной условной логики :

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

    <xsl:template match="/">
        <xsl:apply-templates select="*/node/subnodeA"/>
        <xsl:apply-templates select="*/node/subnodeB"/>
    </xsl:template>

    <xsl:template match="*[subnodeB]/subnodeA">
        <xsl:value-of select="concat(., ' ')"/>
        <xsl:number level="any" count="*[subnodeB]/subnodeA"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

    <xsl:template match="subnodeB">
        <xsl:value-of select="concat(position(), ' ', ., '&#xA;' )"/>
    </xsl:template>

    <xsl:template match="text()">
        <xsl:value-of select="concat(., '&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>

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

<entry>
    <node>
        <subnodeA>test_subnodeA1</subnodeA>
        <subnodeB>test_subnodeB1</subnodeB>
    </node>
    <node>
        <subnodeA>test_subnodeA2</subnodeA>
        <subnodeB>test_subnodeB2</subnodeB>
    </node>
    <node>
        <subnodeA>test_subnodeA3</subnodeA>
    </node>
    <node>
        <subnodeA>test_subnodeA4</subnodeA>
        <subnodeB>test_subnodeB3</subnodeB>
    </node>
</entry>

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

test_subnodeA1 1
test_subnodeA2 2
test_subnodeA3
test_subnodeA4 3
1 test_subnodeB1
2 test_subnodeB2
3 test_subnodeB3
0 голосов
/ 12 августа 2011

Мы можем использовать счетчик, также применяя шаблоны непосредственно на заинтересованном узле:

С учетом подузла A в качестве текущего узла:

  • count(preceding::subnodeB[not(../subnodeA)]) считать все предыдущие узлы без подузла A (текущий сдвиг)
  • count(preceding::subnodeA[../subnodeB]) подсчитать все предыдущие узлы с подузлом B (относительная позиция - 1)

Давайте посмотрим, как он может работать с повторениями (только для подузла A, поскольку подузел B тривиален):

<xsl:template match="entry">

    <xsl:for-each select="node/subnodeA">
        <xsl:value-of select="."/>
        <xsl:if test="../subnodeB">
            <xsl:value-of select="
                count(preceding::subnodeB[not(../subnodeA)])
                + count(preceding::subnodeA[../subnodeB])
                + 1 "/>
        </xsl:if>
        <xsl:if test="position()!=last()">
            <xsl:value-of select="'&#xA;'"/>
        </xsl:if>
    </xsl:for-each>

</xsl:template>

и с применяемыми шаблонами:

<xsl:template match="entry">
    <xsl:apply-templates select="node/subnodeA"/>
</xsl:template>

<xsl:template match="node/subnodeA">
    <xsl:value-of select="."/>
    <xsl:if test="../subnodeB">
        <xsl:value-of select="
            count(preceding::subnodeB[not(../subnodeA)])
            + count(preceding::subnodeA[../subnodeB])
            + 1 "/>
    </xsl:if>
    <xsl:if test="position()!=last()">
        <xsl:value-of select="'&#xA;'"/>
    </xsl:if>
</xsl:template>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...