You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用XSL Transformation解析同名列?附SOAP XML示例

用XSLT解析带同名列的SOAP XML数据

嘿,我来帮你搞定这个问题!先看你给的SOAP响应,核心有俩难点:一是processOCIMessageReturn节点里的内容是CDATA包裹的嵌套XML,XSLT默认不会自动解析它;二是要处理嵌套XML里的同名列。我分步骤给你拆解:

第一步:把CDATA里的嵌套XML提取出来

首先得把CDATA里的XML转成XSLT能处理的节点树。如果用XSLT 2.0+(现在大部分工具都支持,比如Saxon),直接用parse-xml函数就行,非常方便:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:bs="urn:com:broadsoft:webservice">

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

    <!-- 匹配SOAP的Body节点,从这里开始处理 -->
    <xsl:template match="/soapenv:Envelope/soapenv:Body">
        <!-- 提取CDATA文本,解析成XML节点树 -->
        <xsl:variable name="inner-xml" select="parse-xml(bs:processOCIMessageResponse/bs:processOCIMessageReturn/text())"/>
        
        <!-- 把解析后的XML传给下一个模板处理 -->
        <xsl:apply-templates select="$inner-xml"/>
    </xsl:template>

</xsl:stylesheet>

要是你只能用XSLT 1.0,那就得借助扩展函数(比如EXSLT的node-set)配合第三方解析工具了,不过还是优先用XSLT 2.0+,省好多事。

第二步:处理嵌套XML里的同名列

同名列的情况分两种,我分别给你说怎么处理:

情况1:同名列属于不同命名空间

看你的示例里有<sessionId xmlns="",这就是空命名空间的节点,而外层MyDocument属于C命名空间。这种情况咱们用命名空间前缀来区分就行:

<!-- 接着上面的样式表,添加处理MyDocument的模板 -->
<xsl:template match="*:MyDocument">
    <ExtractedData>
        <!-- 空命名空间的sessionId,直接写节点名就行 -->
        <SessionID>
            <xsl:value-of select="sessionId"/>
        </SessionID>
        <!-- 如果C命名空间下也有同名的sessionId,用前缀调用(先在样式表里声明前缀xmlns:c="C") -->
        <NamespaceSessionID>
            <xsl:value-of select="c:sessionId"/>
        </NamespaceSessionID>
    </ExtractedData>
</xsl:template>

这里*:是通配所有命名空间,要是你明确知道命名空间URI,直接声明前缀更清晰。

情况2:同一命名空间下的多个同名列

如果是同一个命名空间里有多个同名节点(比如多个<item>),咱们可以用位置索引或者节点属性来区分:

<!-- 假设MyDocument下有多个c:item节点 -->
<xsl:template match="c:MyDocument">
    <AllItems>
        <!-- 遍历所有item,用position()标记位置 -->
        <xsl:for-each select="c:item">
            <Item index="{position()}">
                <xsl:value-of select="."/>
            </Item>
        </xsl:for-each>
        
        <!-- 如果节点有属性区分,比如type属性,直接过滤 -->
        <UserItems>
            <xsl:value-of select="c:item[@type='user']"/>
        </UserItems>
    </AllItems>
</xsl:template>

完整的示例样式表

把上面的步骤整合起来,完整的XSLT如下,你可以直接拿去测试:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:bs="urn:com:broadsoft:webservice"
                xmlns:c="C">

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

    <xsl:template match="/">
        <xsl:apply-templates select="soapenv:Envelope/soapenv:Body"/>
    </xsl:template>

    <xsl:template match="soapenv:Body">
        <xsl:variable name="inner-xml" select="parse-xml(bs:processOCIMessageResponse/bs:processOCIMessageReturn/text())"/>
        <xsl:apply-templates select="$inner-xml"/>
    </xsl:template>

    <xsl:template match="c:MyDocument">
        <ParsedResult>
            <!-- 提取空命名空间的sessionId -->
            <SessionID>
                <xsl:value-of select="sessionId"/>
            </SessionID>
            <!-- 处理同一命名空间下的多个同名列 -->
            <Items>
                <xsl:for-each select="c:item">
                    <Item position="{position()}">
                        <xsl:value-of select="."/>
                    </Item>
                </xsl:for-each>
            </Items>
        </ParsedResult>
    </xsl:template>

    <!-- 匹配无关的文本和属性,避免默认输出冗余内容 -->
    <xsl:template match="text()|@*"/>

</xsl:stylesheet>

几个关键提醒

  • 命名空间别忘声明:样式表里一定要把所有用到的命名空间都声明好,不然XSLT会找不到节点。
  • CDATA解析的版本差异:XSLT 1.0处理CDATA麻烦很多,尽量用2.0+版本的处理器。
  • 同名列区分优先级:优先用命名空间、节点属性来区分,实在不行再用位置索引,这样代码更健壮。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:29:58