检索XML节点位置之间的值

时间:2013-05-28 13:03:54

标签: xml xslt xpath xslt-2.0

我正在尝试从前面的元素中检索值。但是我尝试检索的值需要在某个位置之后和其他节点位置之前。我怎么能这样做?

示例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中获得正确的测试时有点困惑。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

我建议您使用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>

答案 1 :(得分:0)

只是因为我说过,这里是一个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>