如何跟踪从一个模板到另一个模板的位置?

How to keep track of position from a template to a different template?

我想在 svg 中表示以下 XML,但我目前在跟踪方面遇到问题

时的当前位置

在一个simplePath中,一个点之间的距离是50

从 simplePath 到 Jump,反之亦然,距离为 200


<root>
        <simplePath>
            <point>A</point>
            <point>B</point>
            <point>C</point>
        </simplePath>

        <jump>
            <simplePath>
                <point>D</point>
                <point>E</point>
                <point>F</point>
             </simplePath>
        </jump>

        <simplePath>
            <point>G</point>
        </simplePath>

</root>

XML 的输出应该是:

A : 0
B : 50
C : 100

D : 300
E : 350
F : 400

G : 600

当它主要由 simplePath 组成时,我可以使用 ((position() -1) * 50)

我不知道如何使用

    <xsl:template match="root">
        <xsl:apply-templates select="simplePath | jump"/>
    </xsl:template>

    <xsl:template match="simplePath">
        <xsl:apply-templates select="point"/>
    </xsl:template>

    <xsl:template match="point">
        <xsl:value-of select="."/>
        <xsl:value-of select="(position() - 1)* 50"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="jump">
        <xsl:apply-templates select="simplePath"/>
    </xsl:template>

输出:

A0
B50
C100
D0
E50
F100
G0

如果我正确理解要求,使用 XSLT 3(例如 Saxon 9.8 或更高版本、Saxon-JS 2、Altova XML 2017 R3 或更高版本)和一个累加器:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="3.0"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                exclude-result-prefixes="#all"
                expand-text="yes">
  
  <xsl:accumulator name="distance" as="item()*" initial-value="0, ()">
    <xsl:accumulator-rule match="point" select="if ($value[2] = 'point') then ($value[1] + 50, local-name()) else if ($value[2] = 'jump') then ($value[1] + 200, 'point') else ($value[1], local-name())"/>
    <xsl:accumulator-rule match="jump" select="$value[1], local-name()"/>
    <xsl:accumulator-rule match="jump" phase="end" select="$value[1], local-name()"/>
  </xsl:accumulator>
  
  <xsl:template match="point">
    <xsl:comment>accumulator-before('distance') : {accumulator-before('distance')[1]}</xsl:comment>
    <xsl:next-match/>
  </xsl:template>

  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:mode on-no-match="shallow-copy" use-accumulators="distance"/>

</xsl:stylesheet>

样本输出:

<root>
   <simplePath><!--accumulator-before('distance') : 0-->
      <point>A</point>
      <!--accumulator-before('distance') : 50-->
      <point>B</point>
      <!--accumulator-before('distance') : 100-->
      <point>C</point>
   </simplePath>
   <jump>
      <simplePath><!--accumulator-before('distance') : 300-->
         <point>D</point>
         <!--accumulator-before('distance') : 350-->
         <point>E</point>
         <!--accumulator-before('distance') : 400-->
         <point>F</point>
      </simplePath>
   </jump>
   <simplePath><!--accumulator-before('distance') : 600-->
      <point>G</point>
   </simplePath>
</root>

这里有一个相对简单的查看方式:

XSLT 2.0

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

<xsl:template match="/root">
    <xsl:call-template name="process">
        <xsl:with-param name="points" select=".//point"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="process">
    <xsl:param name="points" />
    <xsl:param name="total" select="0"/>
    <!-- output -->
    <xsl:value-of select="$points[1]"/>
    <xsl:text> : </xsl:text>
    <xsl:value-of select="$total"/>
    <!-- recursive call -->
    <xsl:if test="count($points) > 1">
        <xsl:text>&#10;</xsl:text>
        <xsl:call-template name="process">
            <xsl:with-param name="points" select="$points[position() > 1]"/>
            <xsl:with-param name="total" select="if(boolean($points[1]/ancestor::jump) != boolean($points[2]/ancestor::jump)) then $total + 200 else $total + 50"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

应用于您的输入示例,这将 return:

结果

A : 0
B : 50
C : 100
D : 300
E : 350
F : 400
G : 600