XSL 1.0 节点集的复杂选择 [英] Complex selection of XSL 1.0 node set

查看:25
本文介绍了XSL 1.0 节点集的复杂选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(这个问题是我的问题的简化版本.可以找到已经回答的更简化版本here.由于 michael.hor257k 的评论,我发布了这个更复杂的问题,他建议可能有一种替代方法可以解决它 - 可能在循环中使用 select,或者可能是完全不同的方法.)

(This question is a less simplified version of my problem. The more simplified version which was already answered can be found here. I'm posting this more complicated question due to a comment by michael.hor257k who suggested that there may be an alternative approach that could solve it - possibly using select in a loop, or possibly a completely different approach.)

我想处理一个我无法控制其格式的 XML 文件,以生成 C++ 代码.我需要以几种不同的方式处理在 XML 中定义的函数以生成代码的不同部分.作为其中的一部分,我需要选择匹配复杂条件的函数参数子集,并将此选择传递给多个命名模板;命名模板需要能够访问原始文档.

I'd like to process an XML file, over whose format I have no control, to generate C++ code. I need to process functions defined in XML in several different ways to produce different parts of the code. As part of this I need to select a subset of function parameters that match a complicated criteria and pass this selection to several named templates; the named templates need to be able to access the original document.

此示例使用GenerateNonFixedParameters"模板创建了一个复杂的 C++ 函数参数选择,这些参数没有常数值(即相同的最小值和最大值),其中最小值和最大值可以是十进制或十六进制.参数引用位于文档其他位置的枚举,这些定义由命名模板调用ListParameterValues"引用.

This example creates a complex selection of C++ function parameters that do not have constant values (ie the same min and max), where the min and max may be decimal or hexadecimal, using the "GenerateNonFixedParameters" template. The parameters refer to enumerations which are located elsewhere in the document, and these definitions are referenced by the named template call "ListParameterValues".

有两个问题.

  1. 变量nonFixedParameters"的创建不使用select.我不知道如何在如此复杂的情况下使用 select (XSL 1.0),但也许有办法.

  1. The creation of the variable "nonFixedParameters" does not use select. I cannot work out how to use select for such a complicated case (XSL 1.0), but maybe there is a way.

节点的副本是不够的,因为当前的ListParameterValues"模板需要对文档中的一组原始节点进行操作.

A copy of the nodes does not suffice, as the "ListParameterValues" template as it currently stands needs to operate on an original set of nodes from the document.

标有这两个问题位置的 XSL 示例:

Example XSL with the locations of these two problems marked:

<?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 &gt;= $min and @val &lt;= $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) &gt; 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) &lt;= 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:

Simple XML to feed the above:

<?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 具有相同的值.

Wanted output. Note that p1 has all names within [min..max] listed, but p2 has none listed because min and max have the same value.

p1[secondValue thirdValue forthValue ] p2[]

推荐答案

我认为如果您使用像 exsl:node-set 这样的扩展函数进行转换,您的样式表可以与 XSLT 1.0 一起使用您的结果树片段到节点集,如果您将主输入树的根节点存储到全局变量或参数中,那么您将能够将主输入文档中的节点与新构建的临时树的节点进行比较.

I think your stylesheet can be made to work with XSLT 1.0 if you use an extension function like exsl:node-set to convert your result tree fragment into a node-set and if you store the root node of the primary input tree into a global variable or parameter as then you will be able to compare nodes in your primary input document to nodes of the newly constructed, temporary tree.

根据这些建议,代码看起来像

Based on these suggestions the code would look like

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:exsl="http://exslt.org/common">
    <xsl:output method="text" encoding="iso-8859-1" omit-xml-declaration="yes" />

    <xsl:variable name="main-root" select="/"/>

    <xsl:template match="/">
        <xsl:for-each select="//function">
            <!-- 1. Using exsl:node-set or similar you can convert that result tree fragment into a node set to process it further -->
            <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 xmlns:ms="urn:schemas-microsoft-com:xslt" select="ms:node-set($parameters)/parameter"> for MSXML or XslTransform -->
        <xsl:for-each select="exsl:node-set($parameters)/parameter">
            <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 using the global variable works. -->
            <xsl:for-each select="$main-root//enum[@name=current()/@enum]/value">
                <xsl:if test="@val &gt;= $min and @val &lt;= $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">
                <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) &gt; 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) &lt;= 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>

该示例位于 http://xsltransform.net/94hvTzi/1.

这篇关于XSL 1.0 节点集的复杂选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆