使用 XSL 从 CDATA 转换 XML

Posted

技术标签:

【中文标题】使用 XSL 从 CDATA 转换 XML【英文标题】:Transform XML from CDATA using XSL 【发布时间】:2013-09-07 21:21:07 【问题描述】:

我有这个 XML 文档:

<ns0:getDataResponse xmlns:ns0="http://abc.com/">
    <return>
        <wrapper>
            <data><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
                            <ConDic>
                            <dictionary>bank</dictionary>
                                <rows>
                                    <row>
                                      <bic>ABKZKZKX</bic>
                                      <bcode>319</bcode>
                                      <name1>AA &quot;A BANK&quot;</namekz>
                                      <name2>BB &quot;B BANK&quot;</nameru>
                                    </row>
                                    <row>
                                      <bic>ABNAKZKX</bic>
                                      <bcode>922</bcode>
                                      <name1>CC &quot;C BANK&quot;</namekz>
                                      <name2>DD &quot;D BANK&quot;</nameru>
                                    </row>
                                </rows>
                            </ConDic>]]></data>
        </wrapper>
    </return>
</ns0:getDataResponse>

如何使用 XSL 解析它以获取 CDATA 中的每一行以进行这种选择:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://abc.com/">
<xsl:output method="html" />
<xsl:template match="text()|@*"/>
<xsl:template match="ns0:rows">

<select name="bank" id="bank" class="input" style="width: 370px;">
    <xsl:for-each select="row">
        <xsl:sort select="name1"/>
        <option value="bic"><xsl:value-of select="name1" /></option>
    </xsl:for-each>
</select>

【问题讨论】:

您使用哪种 XSLT 处理器?您需要具有 3.0 功能 w3.org/TR/2013/CR-xpath-functions-30-20130108/#func-parse-xml 的 XSLT 3.0 处理器,或者您需要支持 saxonica.com/documentation/index.html#!functions/saxon/parse 等扩展功能的 XSLT 1.0 或 2.0 处理器。 转义标记不是有效的 XML。没有定义实体&amp;quot;,也没有DTD 引用。即使您使用第一次转换通过禁用输出转义来提取 CDATA,您也会留下不会解析为 XML 的无效标记。此外,它还包括 XML 声明,这使得在 DOE 转换中添加 DTD 引用变得更加困难。有没有办法让 web 服务的生产者改变他们的输出? w3.org/TR/xml/#sec-predefined-ent 定义了 quot,因此不必有一个 DTD 来定义它。 @MartinHonnen,我认为您应该将您的评论放入答案中,以便该问题不再属于“未回答”类别。 @Yoldar-Zi,我的评论有帮助吗?您可以使用提供此类扩展或提供 XSLT 3.0 支持的处理器吗? 【参考方案1】:

按照评论中的建议,我发布了一个解决方案。该示例使用 XSLT 3.0 和 Saxon 9.5(需要 PE 或 EE 版本):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:ns0="http://abc.com/"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math ns0"
    version="3.0">


    <xsl:output method="html" indent="yes"/>

    <xsl:template match="text()|@*"/>

    <xsl:template match="ns0:getDataResponse">

        <select name="bank" id="bank" class="input" style="width: 370px;">
            <xsl:for-each select="parse-xml(return/wrapper/data)//row">
                <xsl:sort select="name1"/>
                <option value="bic">
                    <xsl:value-of select="name1"/>
                </option>
            </xsl:for-each>
        </select>
    </xsl:template>

</xsl:stylesheet>

输入

<ns0:getDataResponse xmlns:ns0="http://abc.com/">
    <return>
        <wrapper>
            <data><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
                            <ConDic>
                            <dictionary>bank</dictionary>
                                <rows>
                                    <row>
                                      <bic>ABKZKZKX</bic>
                                      <bcode>319</bcode>
                                      <name1>AA &quot;A BANK&quot;</name1>
                                      <name2>BB &quot;B BANK&quot;</name2>
                                    </row>
                                    <row>
                                      <bic>ABNAKZKX</bic>
                                      <bcode>922</bcode>
                                      <name1>CC &quot;C BANK&quot;</name1>
                                      <name2>DD &quot;D BANK&quot;</name2>
                                    </row>
                                </rows>
                            </ConDic>]]></data>
        </wrapper>
    </return>
</ns0:getDataResponse>

结果是

<select name="bank" id="bank" class="input" style="width: 370px;">
   <option value="ABKZKZKX">AA "A BANK"</option>
   <option value="ABNAKZKX">CC "C BANK"</option></select>

【讨论】:

【参考方案2】:

如果您有可用的exslt 扩展,您应该能够使用exslt:node-set 创建内容变量。尝试类似(未经测试):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:exsl="http://exslt.org/common"
   extension-element-prefixes="exsl"
   exclude-result-prefixes="exsl"
   version="1.0">

<xsl:template match="/">
    <xsl:variable name="inner" select="exsl:node-set(//data/text())" />
    <select name="bank" id="bank" class="input" style="width: 370px;">
        <xsl:for-each select="$inner//row">
            ...
        </xsl:for-each>
    </select>
</xsl:template>

</xsl:stylesheet>

【讨论】:

node-set() 函数采用 结果树片段 并将其转换为节点集。它不会接受字符串并将其解析为节点集。所以充其量,$inner 的值只是一个字符串,您将无法选择$inner//row

以上是关于使用 XSL 从 CDATA 转换 XML的主要内容,如果未能解决你的问题,请参考以下文章

使用 CDATA 元素对 xml 中的 uuid 进行排序的 xslt 模板

使用从 xml 到 html 的 xsl 转换来维护空格和换行符

XSL 转换后在 PDF 中保留空白

使用 XSL 将 json 转换为 XML

使用 XSL 转换合并两个 XML 文件

如何使用 XSL 在 XML 转换中查找随机元素