XSLT表查找和求和(previous-sibling :: ...)?

时间:2010-10-28 17:20:39

标签: xslt

我有一个XML文档,其中包含如下所示的节点:

<Variable name="var1" dataType="INT32"/>
<Variable name="var2" dataType="INT16"/>
<Variable name="var3" dataType="INT8"/>

我可以遍历变量并显示名称和数据类型就好了,但我想显示变量的大小,以及它的偏移量(第一个变量的偏移量为零,第二个变量偏移量等于第一个的大小,第三个偏移量等于前两个的大小。在上面的示例中,var1的大小为4,偏移量为零,var2的大小为2,偏移量为4,var3的大小为1,偏移量为6。

要打印尺寸,这有效:

<xsl:variable name="fieldSize">
  <xsl:choose>
    <xsl:when test="contains(@dataType, 'INT8')">
        <xsl:value-of select="'1'"/>
    </xsl:when>
    <xsl:when test="contains(@dataType, 'INT16')">
        <xsl:value-of select="'2'"/>
    </xsl:when>
    <xsl:when test="contains(@dataType, 'INT32')">
        <xsl:value-of select="'4'"/>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="'unknown'"/></xsl:otherwise>
  </xsl:choose>
</xsl:variable>
<xsl:value-of select="$fieldSize"/>

但是,我不知道如何打印偏移量!如果字段大小是属性,我可以执行以下操作:

<xsl:variable name="offset" select="sum(preceding-sibling::Variable/@fieldSize)"/>

因为它是变量而不是属性,所以我不能对前兄弟姐妹做一笔总和来计算偏移量。我的下一个想法是尝试创建一个可以根据@dataType属性评估大小的表达式,也许我可以将其提供给“sum()”表达式(不知道是否可行)。

我试图为fieldSizes创建一个NodeSet,所以我可以根据属性查找大小:

<xsl:variable name="fieldSizes">
    <i ref="INT8">1</i>
    <i ref="INT16">2</i>
    <i ref="INT32">4</i>
</xsl:variable>

<xsl:value-of select="$fieldSizes[@ref=@dataType]"/>

但是,最后一行在XSLT转换期间导致错误:预计XPath表达式将返回NodeSet。以下所有变体都会导致相同的错误:

<xsl:value-of select="$fieldSizes[@ref='INT8']"/>
<xsl:value-of select="$fieldSizes[@ref=INT8]"/>
<xsl:value-of select="$fieldSizes[1]"/>

如何根据数据类型打印变量的字段大小?一旦有效,我怎样才能计算出偏移值?也许是这样的事情:

<xsl:variable name="offset" select="sum(preceding-sibling::Variable/$fieldSizes[@ref=@dataType])"/>

3 个答案:

答案 0 :(得分:3)

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 exclude-result-prefixes="msxsl" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:variable name="vDigits" select="'0123456789'"/>

 <xsl:template match="Variable">
  <xsl:variable name="vOffset">
     <xsl:call-template name="getOffset"/>
  </xsl:variable>

  <Variable name="{@name}" dataType="{@dataType}"
    size="{translate(@dataType, translate(@dataType,$vDigits,''),'') div 8}"
    offset="{$vOffset}"
  />
 </xsl:template>


 <xsl:template name="getOffset">
  <xsl:variable name="vrtfprevSizes">
   <xsl:for-each select="preceding-sibling::Variable">
     <v size="{translate(@dataType, translate(@dataType,$vDigits,''),'') div 8}"/>
   </xsl:for-each>
  </xsl:variable>

  <xsl:value-of select="sum(msxsl:node-set($vrtfprevSizes)/v/@size)"/>
 </xsl:template>
</xsl:stylesheet>

应用于此XML文档时

<t>
    <Variable name="var1" dataType="INT32"/>
    <Variable name="var2" dataType="INT16"/>
    <Variable name="var3" dataType="INT8"/>
</t>

生成想要的正确结果

<Variable name="var1" dataType="INT32" size="4" offset="0" />
<Variable name="var2" dataType="INT16" size="2" offset="4" />
<Variable name="var3" dataType="INT8" size="1" offset="6" />

请注意

  1. 没有递归

  2. 在XSLT 1.0中,需要xxx:node-set() 将RTF转换为常规节点集。

  3. 由于Variable元素数量非常大,此解决方案很慢,因为计算了多次相同的部分和。但是,由于调用堆栈太深,所以保证不会崩溃。

答案 1 :(得分:2)

两个解决方案:对于两遍转换(因此您可以使用fn:sum()),您将需要node-set()扩展功能; ussing模式。

此样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my">
    <my:s size="4" dataType="INT32"/>
    <my:s size="2" dataType="INT16"/>
    <my:s size="1" dataType="INT8"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Variable/@*[last()]">
        <xsl:call-template name="identity"/>
        <xsl:attribute name="size">
            <xsl:value-of select="document('')/*/my:s
                                     [@dataType = current()/../@dataType]
                                        /@size"/>
        </xsl:attribute>
        <xsl:attribute name="offset">
            <xsl:apply-templates select=".." mode="offset"/>
        </xsl:attribute>
    </xsl:template>
    <xsl:template match="Variable" mode="offset">
        <xsl:param name="pCounter" select="0"/>
        <xsl:variable name="vPrev" select="preceding-sibling::Variable[1]"/>
        <xsl:apply-templates select="$vPrev" mode="offset">
            <xsl:with-param name="pCounter"
                            select="$pCounter + document('')/*/my:s
                                                  [@dataType = $vPrev/@dataType]
                                                     /@size"/>
        </xsl:apply-templates>
        <xsl:if test="not($vPrev)">
            <xsl:value-of select="$pCounter"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

使用此输入:

<root>
    <Variable name="var1" dataType="INT32"/>
    <Variable name="var2" dataType="INT16"/>
    <Variable name="var3" dataType="INT8"/>
</root>

输出:

<root>
    <Variable name="var1" dataType="INT32" size="4" offset="0"></Variable>
    <Variable name="var2" dataType="INT16" size="2" offset="4"></Variable>
    <Variable name="var3" dataType="INT8" size="1" offset="6"></Variable>
</root>

编辑3 :这个样式表(foward模式,更好的性能,借用Dimitre的大小计算)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:template match="*">
        <xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
    </xsl:template>
    <xsl:template match="Variable">
        <xsl:param name="pOffset" select="0"/>
        <xsl:variable name="vSize" 
                      select="substring-after(@dataType,'INT') div 8"/>
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="size">
                <xsl:value-of select="$vSize"/>
            </xsl:attribute>
            <xsl:attribute name="offset">
                <xsl:value-of select="$pOffset"/>
            </xsl:attribute>
        </xsl:copy>
        <xsl:apply-templates select="following-sibling::Variable[1]">
            <xsl:with-param name="pOffset" select="$pOffset + $vSize"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

输出:

<Variable name="var1" dataType="INT32" size="4" offset="0" />
<Variable name="var2" dataType="INT16" size="2" offset="4" />
<Variable name="var3" dataType="INT8" size="1" offset="6" />

答案 2 :(得分:0)

我认为你不能用简单的总和做到这一点,但你可以做的是创建一个带有参数的模板,该参数包含节点列表和前面的偏移量。它计算列表中第一个项目的偏移量,然后使用新偏移量和节点列表中的其余项目递归调用自身。

您可以在w3schools网站上看到示例template with parameters。他们是开始寻找大多数网络技术信息的合理好地方。