且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

将 XML 转换为 XSLT 中的转义文本

更新时间:2023-02-06 15:56:44

您的代码的工作方式是这样的,因为 xsl:value-of 检索 string-value 节点集.

Your code works the way it does because xsl:value-of retrieves the string-value of the node set.

为了做你想做的事,恐怕你必须明确地编码:

To do what you want, I'm afraid that you'll have to code it explicitly:

    <xsl:template match="/">
        <TestElement>
            <xsl:apply-templates mode="escape"/>
        </TestElement>
    </xsl:template>

    <xsl:template match="*" mode="escape">
        <!-- Begin opening tag -->
        <xsl:text>&lt;</xsl:text>
        <xsl:value-of select="name()"/>

        <!-- Namespaces -->
        <xsl:for-each select="namespace::*">
            <xsl:text> xmlns</xsl:text>
            <xsl:if test="name() != ''">
                <xsl:text>:</xsl:text>
                <xsl:value-of select="name()"/>
            </xsl:if>
            <xsl:text>='</xsl:text>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="."/>
            </xsl:call-template>
            <xsl:text>'</xsl:text>
        </xsl:for-each>

        <!-- Attributes -->
        <xsl:for-each select="@*">
            <xsl:text> </xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>='</xsl:text>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="."/>
            </xsl:call-template>
            <xsl:text>'</xsl:text>
        </xsl:for-each>

        <!-- End opening tag -->
        <xsl:text>&gt;</xsl:text>

        <!-- Content (child elements, text nodes, and PIs) -->
        <xsl:apply-templates select="node()" mode="escape" />

        <!-- Closing tag -->
        <xsl:text>&lt;/</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text>&gt;</xsl:text>
    </xsl:template>

    <xsl:template match="text()" mode="escape">
        <xsl:call-template name="escape-xml">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="processing-instruction()" mode="escape">
        <xsl:text>&lt;?</xsl:text>
        <xsl:value-of select="name()"/>
        <xsl:text> </xsl:text>
        <xsl:call-template name="escape-xml">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
        <xsl:text>?&gt;</xsl:text>
    </xsl:template>

    <xsl:template name="escape-xml">
        <xsl:param name="text"/>
        <xsl:if test="$text != ''">
            <xsl:variable name="head" select="substring($text, 1, 1)"/>
            <xsl:variable name="tail" select="substring($text, 2)"/>
            <xsl:choose>
                <xsl:when test="$head = '&amp;'">&amp;amp;</xsl:when>
                <xsl:when test="$head = '&lt;'">&amp;lt;</xsl:when>
                <xsl:when test="$head = '&gt;'">&amp;gt;</xsl:when>
                <xsl:when test="$head = '&quot;'">&amp;quot;</xsl:when>
                <xsl:when test="$head = &quot;&apos;&quot;">&amp;apos;</xsl:when>
                <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise>
            </xsl:choose>
            <xsl:call-template name="escape-xml">
                <xsl:with-param name="text" select="$tail"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

请注意,此解决方案忽略注释节点,并插入不必要的命名空间节点(因为 namespace:: 轴将包括从父级继承的所有节点).但是,关于名称空间,结果引用的 XML 在语义上等同于您在回复中提供的示例(因为这些重复的重新声明并没有真正改变任何东西).

Note that this solution ignores comment nodes, and inserts unneccessary namespace nodes (as namespace:: axis will include all nodes inherited from parent). Regarding namespaces, however, the resulting quoted XML will be semantically equivalent to the example that you provided in your reply (since those repeated redeclarations don't really change anything).

此外,这不会逃避 <?xml ... ?> 声明,仅仅因为它不存在于 XPath 1.0 数据模型中(它不是处理指令).如果在输出中确实需要它,则必须手动插入它(并确保它指定的编码与 XSLT 处理器的序列化编码一致).

Also, this won't escape the <?xml ... ?> declaration, simply because it is not present in XPath 1.0 data model (it's not a processing instruction). If you actually need it in the output, you'll have to insert it manually (and make sure that encoding it specifies is consistent with serialization encoding of your XSLT processor).