Сравните атрибуты с разных уровней во время преобразования xslt

Ниже я создал упрощенный XML-пример того, как выглядит мой xml. У меня есть атрибут, который содержит число, которое я хотел бы посмотреть. Это своего рода счетчик, и при трансформации я хотел бы добавить что-то всякий раз, когда счетчик ++.

Проблема заключается в количестве уровней в моем xml-файле. Здесь я сделал только три, но на самом деле у меня около 8 или, может быть, даже больше. Мне нужно найти способ сравнить текущий узел с предыдущим (или наоборот), но с учетом уровней. Так, например, в приведенном ниже примере узел lvl2 с идентификатором 4 необходимо сравнить с узлом lvl3 с идентификатором 3 просто для того, чтобы узнать, был ли повышен атрибут id.

XML:

<lvl1 id="1">
    <lvl2 id="1">
        <lvl3 id="1"></lvl3>
        <lvl3 id ="2"></lvl3>
    </lvl2>
    <lvl2 id="2">
        <lvl3 id="3"></lvl3>                        
    </lvl2>
    <lvl2 id="4"></lvl2>
</lvl1>

Поскольку глобальные переменные-счетчики не могут быть и речи с xslt, у меня в настоящее время нет идей, и я не могу найти их ни здесь, ни где-либо еще.

вывод будет примерно таким:

  <ul>
<div>id 1</div>
<li>
  <ul>
    <li>
      <ul>
        <li></li>
        <div>id 2</div>
        <li></li>
      </ul>
    </li>
    <li>
      <ul>
        <div>id 3</div>
        <li></li>
      </ul>
    </li>
    <div>id 4</div>
    <li></li>
  </ul>
</li>

here the stylesheet which transforms the xml into the html output but without the divs:

<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
    <ul>
            <xsl:value-of select="@id"/>
            <xsl:apply-templates select="lvl1"/>
    </ul>
</xsl:template>
<xsl:template match="lvl1">
    <li class="{@id}">
        lvl 1
        <ul>
            <xsl:apply-templates select="lvl2"/>
        </ul>
    </li>
</xsl:template>
<xsl:template match="lvl2">
    <li class="{@id}">lvl 2
        <ul>
            <xsl:apply-templates select="lvl3"/>
        </ul>
    </li>
</xsl:template>
<xsl:template match="lvl3">
    <li class="{@id}">lvl 3
    </li>
</xsl:template>


person Tjassens    schedule 14.08.2012    source источник
comment
Можете ли вы привести пример ожидаемого результата? Обратите внимание: я думаю, что это может быть вопрос, аналогичный этому: stackoverflow.com/questions/7964846/   -  person Lukas Eder    schedule 14.08.2012
comment
Я добавил быстрый пример вывода. В настоящее время я просматриваю узлы xml, используя шаблоны для каждого слоя.   -  person Tjassens    schedule 14.08.2012
comment
Не могли бы вы отредактировать вопрос и добавить объяснение правил, которые реализует преобразование? Непонятно, почему и как предоставленный результат (с использованием каких правил) получается из исходного XML-документа.   -  person Dimitre Novatchev    schedule 14.08.2012
comment
Если вы хотите сравнить lvl2/id=4 с lvl3/id=3, вы можете использовать ось preceding, например. (preceding::*/@id)[last()].   -  person LarsH    schedule 14.08.2012
comment
Или, может быть, то, что вы ищете, будет выполнено count(preceding::*[starts-with(local-name(), 'lvl')]) или даже <xsl:number>. Трудно понять, если вы не объясните правила, как просил Димитре.   -  person LarsH    schedule 14.08.2012
comment
Извините за поздний ответ. Я добавил xslt, надеюсь, это то, что вы имеете в виду под объяснением?!   -  person Tjassens    schedule 15.08.2012


Ответы (2)


Если вы примените этот XSLT

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

    <xsl:template match='/'>
        <ul>
            <xsl:apply-templates select='*' />
        </ul>
    </xsl:template>

    <xsl:template match="*">
        <li>
        <div>
            <xsl:value-of select='@id' />
            <xsl:if test='count(*)&gt;0'>
                <ul>
                    <xsl:apply-templates select='*' />
                </ul>
            </xsl:if>
        </div>
        </li>
    </xsl:template>

    </xsl:stylesheet>

вы получаете то, что может быть тем, что вы просите:

    <ul>
      <li>
        <div>1<ul>
            <li>
              <div>1<ul>
                  <li>
                    <div>1</div>
                  </li>
                  <li>
                    <div>2</div>
                  </li>
                </ul>
              </div>
            </li>
            <li>
              <div>2<ul>
                  <li>
                    <div>3</div>
                  </li>
                </ul>
              </div>
            </li>
            <li>
              <div>4</div>
            </li>
          </ul>
        </div>
      </li>
    </ul>
person BxlSofty    schedule 14.08.2012
comment
может быть, мой вопрос просто неверен, потому что я хочу видеть атрибут id только тогда, когда он выше предыдущего. Так что не 3 раза 1 или два раза 2, а только один из каждого. - person Tjassens; 16.08.2012

Вопрос немного сложнее, чем кажется, так как preceding:: здесь не всегда работает, чтобы найти последний @id перед текущим элементом. Вам также нужно проверить @id на родительском элементе и также учитывать корневую позицию. Этот код дает желаемый результат с XML, указанным в вопросе. Смотрите мои комментарии внутри.

<xsl:template match="/">
    <ul>
        <xsl:apply-templates/>
    </ul>
</xsl:template>

<xsl:template match="*">
    <!-- get preceding (or parent) id -->
    <xsl:variable name="lastId">
        <xsl:choose>
            <!-- try to get id from first preceding element -->
            <xsl:when test="preceding::*[1]/@id">
                <xsl:value-of select="preceding::*[1]/@id"/>
            </xsl:when>
            <!-- there could still be ids in parent elements -->
            <xsl:when test="../@id">
                <xsl:value-of select="../@id"/>
            </xsl:when>
            <!-- or this is the root element -->
            <xsl:otherwise>
                <xsl:value-of select="0"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <!-- now compare current and last id -->
    <xsl:if test="@id > $lastId">
        <div>
            <xsl:text>id </xsl:text>
            <xsl:value-of select="@id"/>
        </div>
    </xsl:if>
    <!-- create list item -->
    <li>
        <!-- check for subelements -->
        <xsl:if test="*">
            <ul>
                <xsl:apply-templates/>
            </ul>
        </xsl:if>
    </li>
</xsl:template>

В зависимости от вашего реального сценария вы можете ограничить match="*" значением match="*[starts-with(local-name(),'lvl')]", как это предлагается в комментарии LarsH.

person friedemann_bach    schedule 22.11.2017