2

我要感谢这个社区最近对我的帮助很大。你是我的救星:)

我正在尝试验证数字(x)何时与前一个不同
所以我的列表已排序,首先我有:

<xsl:apply-templates select="house">
    <xsl:sort select="./point/x"/>
</xsl:apply-templates>

重要的部分是:

<xsl:template match="house">

    <xsl:if test="./point/x != preceding-sibling::house[1]/point/x">
        <br/> 
        <xsl:value-of select="preceding-sibling::house[1]/point/x"/>
             value changed to <xsl:value-of select="./point/x"/>        
    </xsl:if>

    <!-- Just printing -->
    <br/>
    <td><xsl:value-of select="./point/x"/></td>
    <td><xsl:value-of select="./point/y"/></td>

</xsl:template>

注意我在打印中有 2 个值,x 和 y,但我只使用 x
我希望输出类似于:
xy
00
01
0 值更改为 1
10
11

不知何故,我的结果得到了错误的前一个数字

00
1 值变为 0
01
0 值变为 1
10
0 值变为 1
11

进展:我发现通过删除排序输出是正确的

00
0 值更改为 1
10
1 值更改为 0
01
0 值更改为 1
11

所以我现在的问题是......我该怎么做这个程序?

4

3 回答 3

3

不是很好,但是如果你必须知道"x 的前一个值按照一定的顺序",那么你必须再次对节点进行排序并获取正确的节点:

<xsl:template match="house">
    <xsl:variable name="current-position" select="position()" />
    <xsl:variable name="preceding-x-ordered">
      <xsl:for-each select="../house">
        <xsl:sort select="point/x" />
        <xsl:if test="position() = $current-position - 1">
          <xsl:value-of select="point/x" />
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:if test="position() &gt; 1 and point/x != $preceding-x-ordered">
        <br/> 
        <xsl:value-of select="$preceding-x-ordered"/>
        <xsl:text> value changed to </xsl:text>
        <xsl:value-of select="point/x"/>        
    </xsl:if>

    <!-- Just printing -->
    <br/>
    <td><xsl:value-of select="point/x"/></td>
    <td><xsl:value-of select="point/y"/></td>
</xsl:template>

XSLT 应该没有副作用,这意味着处理器可以按照它希望的任何顺序处理模板,而不会改变整体结果。不“知道”它在循环的最后一次迭代中做了什么是该要求的结果。因此,preceding-sibling轴无法知道之前处理了哪个节点。

于 2012-04-06T15:38:29.950 回答
3

这是一个更有效的(线性 vs O(N^2*log(N)) XSLT 1.0 解决方案

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

 <xsl:key name="kHouseById" match="house" use="generate-id()"/>

 <xsl:variable name="vIdsOfSorted">
  <xsl:for-each select="/*/house">
    <xsl:sort select="point/x" data-type="number"/>
    <xsl:copy-of select="concat(generate-id(), ' ')"/>
  </xsl:for-each>
 </xsl:variable>


 <xsl:template match="/*">
  <xsl:call-template name="traverseIds">
   <xsl:with-param name="pIds" select="concat($vIdsOfSorted, ' ')"/>
   <xsl:with-param name="pLength" select="count(house)"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="traverseIds">
   <xsl:param name="pIds"/>
   <xsl:param name="pLength"/>
   <xsl:param name="pPos" select="1"/>
   <xsl:param name="pPrevId" select="''"/>

   <xsl:if test="not($pPos > $pLength)">
     <xsl:variable name="vId" select="substring-before($pIds, ' ')"/>
     <xsl:variable name="vHouse" select="key('kHouseById', $vId)"/>

     <xsl:value-of select=
      "concat('&#xA;(', $vHouse/point/x, ',', $vHouse/point/y, ')')"/>
       <xsl:if test="not($pPos >= $pLength)">
        <xsl:variable name="vNextId" select=
         "substring-before(substring-after($pIds, ' '), ' ')"/>

        <xsl:variable name="vNextHouse" select="key('kHouseById', $vNextId)"/>

        <xsl:if test="not($vHouse/point/x = $vNextHouse/point/x)">
         <xsl:value-of select=
           "concat('&#xA;value changed to ', $vNextHouse/point/x)"/>
        </xsl:if>

        <xsl:call-template name="traverseIds">
          <xsl:with-param name="pIds" select="substring-after($pIds, ' ')"/>
          <xsl:with-param name="pLength" select="$pLength"/>
                <xsl:with-param name="pPos" select="$pPos+1"/>
                <xsl:with-param name="pPrevId" select="$vId"/>
        </xsl:call-template>
       </xsl:if>
     </xsl:if>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时:

<t>
  <house>
    <point>
      <x>1</x><y>0</y>
    </point>
  </house>
  <house>
    <point>
      <x>0</x><y>1</y>
    </point>
  </house>
  <house>
    <point>
      <x>0</x><y>0</y>
    </point>
  </house>
  <house>
    <point>
      <x>1</x><y>1</y>
    </point>
  </house>
</t>

产生了想要的正确结果

(0,1)
(0,0)
value changed to 1
(1,0)
(1,1)

如果您不喜欢递归,这里有一个使用xxx:node-set()扩展函数的非递归 XSLT 1.0 解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output method="text"/>
 <xsl:variable name="vrtfSorted">
        <xsl:for-each select="/*/house">
            <xsl:sort select="point/x" data-type="number"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
 </xsl:variable>

 <xsl:variable name="vSorted" select="ext:node-set($vrtfSorted)/*"/>

    <xsl:template match="/*">
        <xsl:for-each select="$vSorted">
            <xsl:variable name="vPos" select="position()"/>
            <xsl:if test=
              "$vPos > 1 
             and 
               not(point/x = $vSorted[position()=$vPos -1]/point/x)">
                <xsl:text>&#xA;</xsl:text>
                <xsl:value-of select="concat('value changed to ', point/x)"/>
            </xsl:if>
            <xsl:text>&#xA;</xsl:text>
            <xsl:value-of select="concat('(',point/x,',',point/y, ')')"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

产生完全相同的正确结果


二、XSLT 2.0 解决方案

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

 <xsl:template match="/*">
  <xsl:variable name="vSorted">
   <xsl:perform-sort select="house">
     <xsl:sort select="xs:integer(point/x)"/>
   </xsl:perform-sort>
  </xsl:variable>

  <xsl:sequence select=
   "(for $n   in 2 to count($vSorted/*),
        $n-1 in $n -1,
        $vNewX in $vSorted/*[$n]/point/x,
        $vOldX in $vSorted/*[$n-1]/point/x,
        $vNewY in $vSorted/*[$n]/point/y,
        $vOldY in $vSorted/*[$n-1]/point/y,
        $remark in
              if($vNewX ne $vOldX)
                then concat('&#xA;value changed to ', $vNewX, '&#xA;')
                else '&#xA;'
     return
       concat('(',$vOldX,',',$vOldY, ')', $remark)
     ),
     concat('(',$vSorted/*[last()]/point/x,',',
                $vSorted/*[last()]/point/y, ')'
            )
   "/>
 </xsl:template>
</xsl:stylesheet>

当应用于相同的 XML 文档(上图)时,会产生相同的正确结果:

 (0,1)
 (0,0)
value changed to 1
 (1,0)
 (1,1)
于 2012-04-07T02:43:43.993 回答
2

这是另一种方法。你可能想从组的角度来考虑。正如您已标记问题 xslt 2.0,这确实带有一些有用的分组功能(在 xslt 1.0 中,您必须使用 Meunchian 分组技术)

实际上,您是按点/x值对房屋进行分组,因此您可以像这样得到每个不同的组:

<xsl:for-each-group select="house" group-by="point/x">
   <xsl:sort select="point/x"/>

然后你可以像这样遍历组内的房屋:

<xsl:for-each select="current-group()">
    <xsl:sort select="point/y"/>

这是完整的 XSLT

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

   <xsl:template match="houses">
      <xsl:text>xy&#13;</xsl:text>
      <xsl:for-each-group select="house" group-by="point/x">
         <xsl:sort select="point/x"/>
         <xsl:if test="position() &gt; 1">
            <xsl:value-of select="concat(' value changed to ', point/x, '&#13;')" />
         </xsl:if>
         <xsl:for-each select="current-group()">
            <xsl:sort select="point/y"/>
            <xsl:value-of select="concat(point/x, point/y, '&#13;')" />
         </xsl:for-each>
         <xsl:if test="position() &lt; last()">
            <xsl:value-of select="point/x" />
         </xsl:if>
      </xsl:for-each-group>
   </xsl:template>
</xsl:stylesheet>

当应用于以下 XML....

<houses>
    <house><point><x>1</x><y>0</y></point></house>
    <house><point><x>2</x><y>1</y></point></house>
    <house><point><x>0</x><y>1</y></point></house>
    <house><point><x>1</x><y>1</y></point></house>
    <house><point><x>0</x><y>0</y></point></house>
    <house><point><x>2</x><y>0</y></point></house>
</houses>

以下是输出:

xy
00
01
0 value changed to 1
10
11
1 value changed to 2
20
21
于 2012-04-06T22:42:45.467 回答