【发布时间】:2015-02-23 11:57:55
【问题描述】:
(这个问题是我的问题的一个不太简化的版本。可以找到已经回答的更简化的版本here。由于 michael.hor257k 的评论,我发布了这个更复杂的问题,他建议那里可能是可以解决它的替代方法 - 可能在循环中使用 select,或者可能是完全不同的方法。)
我想处理一个我无法控制其格式的 XML 文件,以生成 C++ 代码。我需要以几种不同的方式处理 XML 中定义的函数,以生成代码的不同部分。作为其中的一部分,我需要选择与复杂标准匹配的函数参数子集,并将此选择传递给多个命名模板;命名模板需要能够访问原始文档。
本示例使用“GenerateNonFixedParameters”模板创建了一个复杂的 C++ 函数参数选择,这些参数没有常量值(即相同的最小值和最大值),其中最小值和最大值可以是十进制或十六进制。参数引用位于文档中其他位置的枚举,这些定义由命名模板调用“ListParameterValues”引用。
有两个问题。
变量“nonFixedParameters”的创建不使用select。对于如此复杂的情况(XSL 1.0),我不知道如何使用 select,但也许有办法。
节点的副本是不够的,因为目前的“ListParameterValues”模板需要对文档中的一组原始节点进行操作。
示例 XSL 标记了这两个问题的位置:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:for-each select="//function">
<!-- 1. This does not use 'select' therefore it does not work. This is XSL 1.0 so as="node()*" cannot be used. -->
<xsl:variable name="nonFixedParameters">
<xsl:call-template name="GenerateNonFixedParameters"/>
</xsl:variable>
<xsl:call-template name="ListParameterValues">
<xsl:with-param name="parameters" select="$nonFixedParameters"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="ListParameterValues">
<xsl:param name="parameters"/>
<xsl:for-each select="$parameters">
<xsl:value-of select="@name"/>
<xsl:text>[</xsl:text>
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="@min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="@max" />
</xsl:call-template>
</xsl:variable>
<!-- 2. This must be executed in the context of a document node, therefore this does not work. -->
<xsl:for-each select="//enum[@name=current()/@enum]/value">
<xsl:if test="@val >= $min and @val <= $max">
<xsl:value-of select="@name"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>] </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="GenerateNonFixedParameters">
<xsl:for-each select="parameter">
<xsl:variable name="min">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="@min" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="max">
<xsl:call-template name="ToNum">
<xsl:with-param name="hexOrNum" select="@max" />
</xsl:call-template>
</xsl:variable>
<xsl:if test="$min != $max">
<!-- Here a copy is clearly the wrong approach! -->
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="HexToNum">
<xsl:param name="hex" />
<xsl:param name="num" select="0"/>
<xsl:param name="msb" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
<xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $msb))"/>
<xsl:param name="result" select="16 * $num + $value"/>
<xsl:if test="string-length($hex) > 1">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring($hex, 2)"/>
<xsl:with-param name="num" select="$result"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($hex) <= 1">
<xsl:value-of select="$result"/>
</xsl:if>
</xsl:template>
<xsl:template name="ToNum">
<xsl:param name="hexOrNum" />
<xsl:if test="starts-with($hexOrNum, '0x')">
<xsl:call-template name="HexToNum">
<xsl:with-param name="hex" select="substring-after($hexOrNum, '0x')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(starts-with($hexOrNum, '0x'))">
<xsl:value-of select="$hexOrNum"/>
</xsl:if>
</xsl:template>
</xsl:transform>
提供上述内容的简单 XML:
<?xml version="1.0" encoding="UTF-8"?>
<body>
<dictionary>
<enum name="EnumName">
<value name="firstValue" val="1" />
<value name="secondValue" val="2" />
<value name="thirdValue" val="3" />
<value name="forthValue" val="4" />
<value name="fifthValue" val="5" />
</enum>
</dictionary>
<function name="FunctionOne">
<parameter name="p1" type="enum" enum="EnumName" min="2" max="0x4"/>
<parameter name="p2" type="enum" enum="EnumName" min="0x03" max="3"/>
</function>
</body>
想要的输出。请注意 p1 列出了 [min..max] 内的所有名称,但 p2 没有列出任何名称,因为 min 和 max 具有相同的值。
p1[secondValue thirdValue forthValue ] p2[]
【问题讨论】:
-
如果您需要构造一个结果树片段,然后将其作为一个节点集进行处理,那么 XSLT 1.0 处理器确实支持扩展功能。当然,如果您需要比较来自不同文档的节点(例如中间文档和主输入文档),您可以将主文档存储在变量或参数中。所以xsltransform.net/94hvTzi/1 应该让您了解哪些 XSLT 技术有助于处理结果树片段和不同的文档。我将此作为评论发布,因为它是仅代码/链接的建议。
-
如果您使用 MSXML 则不支持
exsl:node-set,您可以改用<xsl:for-each xmlns:ms="urn:schemas-microsoft-com:xslt" select="ms:node-set($parameters)/parameter">。 -
查看当前的 XSLT,调用模板
GenerateNonFixedParamaters,在其中迭代parameter元素。然后,您获取此结果并将选定的参数传递给ListParameterValues,在其中迭代选定的参数并重新计算在GenerateNonFixedParamaters中计算的相同“最小值”和“最大值”值。或许你可以将两个模板合二为一,这样一计算最小值和最大值就立即输出参数;例如xsltransform.net/eiZQaFn -
使用 Martin Honnen 所建议的“节点集”几乎可以肯定是最好的方法。一个不太优雅的替代方法是返回参数的“@name”,并使用字符串函数
contains选择具有匹配名称的参数。看到这个:xsltransform.net/gWmuiJp/1