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

使用XSLT 3.0将多记录SOAP XML转换为JSON的问题求助

SOAP XML转JSON的XSLT 3.0实现问题

我有一个包含多条记录的SOAP XML,需要提取ReportId、PolicyNumber等字段,且每条主记录下的sf:Case_Responses_GCC__r标签内还有多条子记录。之前用XSLT 1.0完成了转换,但现在用支持XSLT 3.0的Saxon-JS时,自己写的XSLT代码报错,求正确的XSLT代码以生成预期的JSON输出。

我的XML数据

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns="urn:enter.soap.force.com"
  xmlns:sf="urn:sobject.enter.soap.force.com"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <queryMoreResponse>
      <result>
        <done>false</done>
        <records xsi:type="sf:Case">
          <sf:ReportId>6896SDRGrt868</sf:ReportId>
          <sf:PolicyNumber>445353566</sf:PolicyNumber>
           <sf:Case_Responses_GCC__r>
             <records xsi:type="sf:Response_GCC__c">
               <sf:Id xsi:nil="true"/>
               <sf:Question_GCC__c>Available to question?</sf:Question_GCC__c>
               <sf:Response_GCC__c>Yes</sf:Response_GCC__c>
             </records>
             <records xsi:type="sf:Response_GCC__c">
              <sf:Id xsi:nil="true"/>
              <sf:Question_GCC__c>Relationship</sf:Question_GCC__c>
              <sf:Response_GCC__c>Self</sf:Response_GCC__c>
             </records>
           </sf:Case_Responses_GCC__r>
        </records>
        <records xsi:type="sf:Case">
          <sf:ReportId>5003L005UCcfVVS</sf:ReportId>
          <sf:PolicyNumber>87768978</sf:PolicyNumber>
           <sf:Case_Responses_GCC__r>
             <records xsi:type="sf:Response_GCC__c">
               <sf:Id xsi:nil="true"/>
               <sf:Question_GCC__c>Available to question?</sf:Question_GCC__c>
               <sf:Response_GCC__c>No</sf:Response_GCC__c>
             </records>
             <records xsi:type="sf:Response_GCC__c">
              <sf:Id xsi:nil="true"/>
              <sf:Question_GCC__c>Relationship</sf:Question_GCC__c>
              <sf:Response_GCC__c>Father</sf:Response_GCC__c>
             </records>
           </sf:Case_Responses_GCC__r>
        </records>
      </result>
    </queryMoreResponse>
  </soapenv:Body>
</soapenv:Envelope>

我尝试的XSLT代码

<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sf="urn:sobject.enter.soap.force.com"
xpath-default-namespace="urn:enter.soap.force.com"
xmlns:json="http://www.w3.org/2005/xpath-functions/" 
expand-text="yes"
exclude-result-prefixes="#all">

    <xsl:output method="json" version="1.0" encoding="utf-8" indent="yes"/>

    <xsl:template match="/soapenv:Envelope">
        <xsl:variable name="jsonObject">
            <lic lang="en">
                <xsl:apply-templates select="soapenv:Body/queryMoreResponse/result/records"/>
            </lic>
        </xsl:variable>
        <xsl:value-of select="json:serialize($jsonObject, map { 'method': 'json', 'indent': true() })"/>
    </xsl:template>
    
    <xsl:template match="sf:records">
        <xsl:map>
            <xsl:map-entry key="reportversion" select="1"/>
            <xsl:map-entry key="reportid" select="sf:ReportId"/>
            <xsl:map-entry key="PolicyNumber" select="sf:PolicyNumber"/>
            <xsl:map-entry key="primarysourcecountry" select="'IN'"/>
            <xsl:map-entry key="client">
                <xsl:map>
                    <xsl:map-entry key="summary">
                        <xsl:variable name="responses">
                            <xsl:apply-templates select="sf:Case_Responses_GCC__r/records"/>
                        </xsl:variable>
                        <xsl:sequence select="json:to-array($responses)"/>
                    </xsl:map-entry>
                </xsl:map>
            </xsl:map-entry>
        </xsl:map>
    </template>
    
    <xsl:template match="sf:records">
        <xsl:map>
            <xsl:map-entry key="sf:Question_GCC__c" select="sf:Question_GCC__c"/>
            <xsl:map-entry key="sf:Response_GCC__c" select="sf:Response_GCC__c"/>
        </xsl:map>
    </template>

</xsl:stylesheet>

预期JSON输出

{
    "lic":[{
            "reportversion":"1",
            "reportid":"5003L000005UCcfQAG",
            "PolicyNumber": "004176451",
            "primarysourcecountry": "IN",
            "client":{"summary": {
                    "Available to question?": "No",
                    "Relationship": "Father"
                }
            }
        },{
            "reportversion":"1",
            "reportid":"5003L000005UCcfQAG",
            "PolicyNumber": "004176451",
            "primarysourcecountry": "IN",
            "client":{"summary": {
                    "Available to question?": "No",
                    "Relationship": "Father"
                }
            }
        }       
    ]
}

解决方案

原代码问题分析

  1. 模板冲突:两个模板都匹配sf:records,Saxon-JS会优先使用最后定义的模板,导致主记录的处理逻辑失效。
  2. 结构不符合预期:预期中summary是一个键值对对象,原代码却试图将子记录转为数组,与需求不符。
  3. 转换方式冗余:通过临时XML变量转JSON的方式不够直接,XSLT 3.0可以直接构造JSON数据结构。

修正后的XSLT代码

<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:sf="urn:sobject.enter.soap.force.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xpath-default-namespace="urn:enter.soap.force.com"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="#all">

    <xsl:output method="json" encoding="utf-8" indent="yes"/>

    <xsl:template match="/soapenv:Envelope">
        <xsl:sequence select="fn:serialize(
            map {
                'lic': soapenv:Body/queryMoreResponse/result/records[@xsi:type='sf:Case'] ! process-case(.)
            },
            map { 'method': 'json', 'indent': true() }
        )"/>
    </xsl:template>

    <!-- 处理主Case记录 -->
    <xsl:function name="process-case" as="map(*)">
        <xsl:param name="case-record" as="element()"/>
        <xsl:sequence select="
            map {
                'reportversion': '1',
                'reportid': string($case-record/sf:ReportId),
                'PolicyNumber': string($case-record/sf:PolicyNumber),
                'primarysourcecountry': 'IN',
                'client': map {
                    'summary': $case-record/sf:Case_Responses_GCC__r/records[@xsi:type='sf:Response_GCC__c'] ! map {
                        string(sf:Question_GCC__c): string(sf:Response_GCC__c)
                    } => map:merge()
                }
            }
        "/>
    </xsl:function>

</xsl:stylesheet>

代码说明

  1. 模板与函数分离:用函数process-case处理每条主记录,逻辑更清晰,避免模板冲突。
  2. 直接构造JSON结构:使用XSLT 3.0的map和fn:serialize直接生成JSON,无需中间XML转换。
  3. 子记录合并为对象:通过map:merge()将所有子记录的问题-响应对合并为一个单一的summary对象,符合预期结构。
  4. 明确类型转换:用string()确保字段值转为字符串类型,避免JSON序列化时出现类型问题。

运行该代码后,会生成与预期结构一致的JSON输出,适配Saxon-JS的XSLT 3.0环境。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:08:10