Получить значение между позициями узла XML

Я пытаюсь получить значения из предыдущих элементов. Но значение, которое я пытаюсь получить, должно быть после определенной позиции и перед позицией других узлов. Как я могу это сделать?

Пример XML:

<?xml version="1.0" encoding="UTF-8"?>
<actions>
    <action>
        <code>tr</code>
        <value>503</value>
    </action>
    <action>
        <code>co</code>
        <value>0</value>
    </action>
    <action>
        <code>cou</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>87</value>
    </action>
    <action>
        <code>st</code>
        <value>0</value>
    </action>
    <action>
        <code>wta</code>
        <value>0</value>
    </action>
    <action>
        <code>pi</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>64</value>
    </action>
    <action>
        <code>st</code>
        <value>0</value>
    </action>
    <action>
        <code>del</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>27</value>
    </action>
    <action>
        <code>wa</code>
        <value>0</value>
    </action>
    <action>
        <code>dec</code>
        <value>0</value>
    </action>
</actions>

Текущий XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="xs fn">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/actions">
        <result>
            <!-- Loop through all action elements -->
            <xsl:for-each select="action">
                <!-- Display only the needed action in the result file -->
                <xsl:if test="code = 'co' or code = 'st' or code = 'dec' or code = 'pi' or code = 'del'">
                    <action>
                        <code>
                            <xsl:choose>
                                <xsl:when test="code = 'co'">1</xsl:when>
                                <xsl:when test="code = 'st'">5</xsl:when>
                                <xsl:when test="code = 'dec'">2</xsl:when>
                                <xsl:when test="code = 'pi'">3</xsl:when>
                                <xsl:when test="code = 'del'">4</xsl:when>
                            </xsl:choose>
                        </code>
                        <!-- Get some positions in variables -->
                        <xsl:variable name="previousPosition"><xsl:value-of select="position() - 1" /></xsl:variable>
                        <xsl:variable name="lastTRPosition"><xsl:value-of select="count((preceding::action[code = 'tr'])[last()]/preceding::action)+1" /></xsl:variable>
                        <xsl:variable name="currentPosition"><xsl:value-of select="position()" /></xsl:variable>
                        <!-- Should be the value of the preceding action element with code 'tr' (last occurence). But only use when between the last preceding action element with code 'tr' and the current node position NO known code is used ('co', 'st', 'dec', 'pi' or 'del') -->
                        <value>
                                    <xsl:choose>
                                        <xsl:when test="(preceding::action[code = 'tr']/value)[last()] != ''"> <!-- some work to do here -->
                                            <xsl:value-of select="round((preceding::action[code = 'tr']/value)[last()])" />
                                        </xsl:when>
                                        <xsl:otherwise>0</xsl:otherwise>
                                    </xsl:choose>
                        </value>
                    </action>
                </xsl:if>
            </xsl:for-each>
        </result>
    </xsl:template>
</xsl:stylesheet>

Текущий результат:

<?xml version="1.0" encoding="UTF-8"?>
<result>
    <action>
        <code>1</code>
        <value>503</value>
    </action>
    <action>
        <code>5</code>
        <value>87</value>
    </action>
    <action>
        <code>3</code>
        <value>87</value>
    </action>
    <action>
        <code>5</code>
        <value>64</value>
    </action>
    <action>
        <code>4</code>
        <value>64</value>
    </action>
    <action>
        <code>2</code>
        <value>27</value>
    </action>
</result>

Желаемый результат:

<?xml version="1.0" encoding="UTF-8"?>
<result>
    <action>
        <code>1</code>
        <value>503</value>
    </action>
    <action>
        <code>5</code>
        <value>87</value>
    </action>
    <action>
        <code>3</code>
        <value>0</value>
    </action>
    <action>
        <code>5</code>
        <value>64</value>
    </action>
    <action>
        <code>4</code>
        <value>0</value>
    </action>
    <action>
        <code>2</code>
        <value>27</value>
    </action>
</result>

Изменения и почему?

<action>
    <code>3</code>
    <value>87</value> <!-- should be 0 -->
</action>

Это должно быть 0. Потому что между позицией последнего action/code = 'tr' и текущей позицией() узла для записи в результате находится известный код 'st', который уже имеет это значение.

<action>
    <code>4</code>
    <value>64</value>
</action>

Это должно быть 0. Потому что между позицией последнего action/code = 'tr' и текущей позицией() узла для записи в результате находится известный код 'st', который уже имеет это значение.

Я немного застрял в получении правильного теста в xsl:when. Может кто-нибудь помочь?


person Mark Veenstra    schedule 28.05.2013    source источник


Ответы (2)


Я бы предложил сгруппировать с group-starting-with, а затем использовать оператор >> для фильтрации. Я также сделал сопоставление параметром и использовал ключ для эффективного сопоставления, поэтому в целом я получаю

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="xs fn">

    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:key name="code" match="code" use="@from"/>

    <xsl:param name="code-map">
      <code from="co" to="1"/>
      <code from="st" to="5"/>
      <code from="dec" to="2"/>
      <code from="pi" to="3"/>
      <code from="del" to="4"/>
    </xsl:param>

    <xsl:template match="/actions">
        <result>
            <xsl:for-each-group select="action" group-starting-with="action[code = 'tr']">
              <xsl:variable name="tr-head" select="."/>
              <xsl:apply-templates select="current-group()[self::action[code = $code-map/code/@from]]">
                <xsl:with-param name="tr" select="$tr-head"/>
              </xsl:apply-templates>
            </xsl:for-each-group>
        </result>
    </xsl:template>

    <xsl:template match="action">
      <xsl:param name="tr"/>
      <xsl:copy>
        <code>
          <xsl:value-of select="key('code', code, $code-map)/@to"/>
        </code>
        <value>
          <xsl:value-of 
            select="if (not(exists((current-group() except $tr) 
                                    [current() >> . and code = $code-map/code/@from])))
                    then $tr/value else 0"/>
        </value>
      </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Когда я использовал Saxon 9.5 для преобразования ввода

<?xml version="1.0" encoding="UTF-8"?>
<actions>
    <action>
        <code>tr</code>
        <value>503</value>
    </action>
    <action>
        <code>co</code>
        <value>0</value>
    </action>
    <action>
        <code>cou</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>87</value>
    </action>
    <action>
        <code>st</code>
        <value>0</value>
    </action>
    <action>
        <code>wta</code>
        <value>0</value>
    </action>
    <action>
        <code>pi</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>64</value>
    </action>
    <action>
        <code>st</code>
        <value>0</value>
    </action>
    <action>
        <code>del</code>
        <value>0</value>
    </action>
    <action>
        <code>tr</code>
        <value>27</value>
    </action>
    <action>
        <code>wa</code>
        <value>0</value>
    </action>
    <action>
        <code>dec</code>
        <value>0</value>
    </action>
</actions>

Я получаю желаемый результат

<result>
   <action>
      <code>1</code>
      <value>503</value>
   </action>
   <action>
      <code>5</code>
      <value>87</value>
   </action>
   <action>
      <code>3</code>
      <value>0</value>
   </action>
   <action>
      <code>5</code>
      <value>64</value>
   </action>
   <action>
      <code>4</code>
      <value>0</value>
   </action>
   <action>
      <code>2</code>
      <value>27</value>
   </action>
</result>
person Martin Honnen    schedule 28.05.2013
comment
Мартин, не могли бы вы объяснить строку <xsl:apply-templates select="current-group()[self::action[code = $code-map/code/@from]]">, потому что я пытаюсь вставить ваше решение в общую таблицу стилей, которая у меня есть, но я не могу заставить ее работать. Похоже, эта строка не дает никакого вывода. См. полный исходный код XML и XSLT здесь: pastebin.com/JC9Gqdgb - person Mark Veenstra; 29.05.2013
comment
Похоже, проблема возникает из-за пространства имен по умолчанию в документе xmlns="http://www.company.com/log/lk/pl". Я не совсем понимаю, почему - person Mark Veenstra; 29.05.2013
comment
@MarkVeenstra, поскольку ваш XSLT определяет пространство имен по умолчанию для элементов, элементы внутри param, которые я настроил, также попадают в это пространство имен, и таким образом путь $code-map/code/@from их не выбирает. Вам нужно поставить <xsl:param name="code-map" xmlns="">...</xsl:param> в параметр, чтобы избежать этой проблемы. - person Martin Honnen; 30.05.2013

Только потому, что я это сказал, здесь решение xslt-1.0.

<?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" encoding="UTF-8" indent="yes"/>

    <xsl:template match="action" />

    <xsl:template match="action [code = 'co' or code = 'st' or code = 'dec' or code = 'pi' or code = 'del']" >
        <result>
            <xsl:copy>
                <code>
                    <xsl:choose>
                        <xsl:when test="code = 'co'">1</xsl:when>
                        <xsl:when test="code = 'st'">5</xsl:when>
                        <xsl:when test="code = 'dec'">2</xsl:when>
                        <xsl:when test="code = 'pi'">3</xsl:when>
                        <xsl:when test="code = 'del'">4</xsl:when>
                    </xsl:choose>
                </code>
            <value>
                <xsl:variable name="ptr" select="preceding-sibling::action[code='tr'][1]"/>
                <xsl:variable name="trpos" select="count($ptr/preceding-sibling::action)"/>
                <xsl:variable name="pcode" select="preceding-sibling::action[code = 'co' or code = 'st' or code = 'dec' or code = 'pi' or code = 'del'][1]"/>
                <xsl:variable name="codepos" select="count($pcode/preceding-sibling::action)"/>
                <xsl:choose>
                    <xsl:when test="$trpos >= $codepos">
                        <xsl:value-of select="round($ptr/value)" />
                    </xsl:when>
                    <xsl:otherwise>0</xsl:otherwise>
                </xsl:choose>

            </value>
            </xsl:copy>
        </result>

    </xsl:template>
    <xsl:template match="/actions">
        <result>
            <xsl:apply-templates select="action" />
        </result>

    </xsl:template>
</xsl:stylesheet>
person hr_117    schedule 28.05.2013