使用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" } } } ] }
解决方案
原代码问题分析
- 模板冲突:两个模板都匹配
sf:records,Saxon-JS会优先使用最后定义的模板,导致主记录的处理逻辑失效。 - 结构不符合预期:预期中
summary是一个键值对对象,原代码却试图将子记录转为数组,与需求不符。 - 转换方式冗余:通过临时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>
代码说明
- 模板与函数分离:用函数
process-case处理每条主记录,逻辑更清晰,避免模板冲突。 - 直接构造JSON结构:使用XSLT 3.0的
map和fn:serialize直接生成JSON,无需中间XML转换。 - 子记录合并为对象:通过
map:merge()将所有子记录的问题-响应对合并为一个单一的summary对象,符合预期结构。 - 明确类型转换:用
string()确保字段值转为字符串类型,避免JSON序列化时出现类型问题。
运行该代码后,会生成与预期结构一致的JSON输出,适配Saxon-JS的XSLT 3.0环境。
内容的提问来源于stack exchange,提问作者Rajat Kumar
相关产品推荐
相关产品推荐

