我有一个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])"/>
答案 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" />
请注意:
没有递归。
在XSLT 1.0中,需要xxx:node-set()
将RTF转换为常规节点集。
由于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。他们是开始寻找大多数网络技术信息的合理好地方。