XSLT实现XML转JSON:根节点缺失与单节点数组化问题解决
XSLT实现XML转JSON的针对性解决方案
当前在使用XSLT将XML转换为JSON时遇到两个问题:
- 根节点
notifications未出现在JSON输出中 - 单个
Notification节点无法在JSON中以数组形式呈现
要求仅允许硬编码notifications和Notification节点名称,其余节点无需硬编码。
输入XML
<notifications xmlns="http://soap.sforce.com/2005/09/outbound"> <OrganizationId>123</OrganizationId> <ActionId>123</ActionId> <SessionId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> <EnterpriseUrl>qwe</EnterpriseUrl> <PartnerUrl>qwe</PartnerUrl> <Notification> <Id>123</Id> <sObject xsi:type="sf:Opportunity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.enterprise.soap.sforce.com"> <sf:Id>ao123</sf:Id> <sf:Amount>60000.0</sf:Amount> <sf:CreatedDate>2014-11-26T14:45:52.000Z</sf:CreatedDate> <sf:IsClosed>false</sf:IsClosed> </sObject> </Notification> </notifications>
预期输出JSON
{ "notifications": { "OrganizationId": "123", "ActionId": "123", "SessionId": { "@nil": "true" }, "EnterpriseUrl": "qwe", "PartnerUrl": "qwe", "Notification": [ { "Id": "123", "sObject": { "@type": "sf:Opportunity", "Id": "ao123", "Amount": "60000.0", "CreatedDate": "2014-11-26T14:45:52.000Z", "IsClosed": "false" } } ] } }
现有尝试的XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" xmlns="http://www.w3.org/2005/xpath-functions" expand-text="yes" version="3.0"> <xsl:output method="text"/> <xsl:template match="/"> <xsl:variable name="json-xml"> <xsl:apply-templates/> </xsl:variable> <xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/> </xsl:template> <xsl:template match="*[not(*)]"> <string key="{local-name()}">{.}</string> </xsl:template> <xsl:template match="*[*]"> <xsl:param name="key" as="xs:boolean" select="false()"/> <map> <xsl:if test="$key"> <xsl:attribute name="key" select="local-name()"/> </xsl:if> <xsl:for-each-group select="*" group-by="node-name()"> <xsl:choose> <xsl:when test="current-group()[2]"> <array key="{local-name()}"> <xsl:apply-templates select="current-group()"> <xsl:with-param name="key" select="false()"/> </xsl:apply-templates> </array> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="current-group()"> <xsl:with-param name="key" select="true()"/> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </map> </xsl:template> </xsl:stylesheet>
解决方案
针对需求修改后的XSLT代码如下:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.enterprise.soap.sforce.com" xmlns:soap="http://soap.sforce.com/2005/09/outbound" exclude-result-prefixes="#all" xmlns="http://www.w3.org/2005/xpath-functions" expand-text="yes" version="3.0"> <xsl:output method="text"/> <!-- 处理根节点,确保notifications作为最外层键 --> <xsl:template match="/"> <xsl:variable name="json-xml"> <map> <xsl:apply-templates select="/soap:notifications" mode="root"/> </map> </xsl:variable> <xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/> </xsl:template> <!-- 根节点notifications的处理 --> <xsl:template match="soap:notifications" mode="root"> <map key="notifications"> <xsl:apply-templates select="*"/> </map> </xsl:template> <!-- 强制Notification节点转为数组,无论数量多少 --> <xsl:template match="soap:Notification"> <array key="Notification"> <xsl:for-each select="."> <map> <xsl:apply-templates select="*"/> </map> </xsl:for-each> </array> </xsl:template> <!-- 处理带xsi:nil属性的节点 --> <xsl:template match="*[@xsi:nil='true']"> <map key="{local-name()}"> <string key="@nil">true</string> </map> </xsl:template> <!-- 处理sObject节点,提取xsi:type属性 --> <xsl:template match="soap:sObject"> <map key="sObject"> <string key="@type">{@xsi:type}</string> <xsl:apply-templates select="sf:*"/> </map> </xsl:template> <!-- 处理sf前缀的子节点,去掉前缀保留本地名称 --> <xsl:template match="sf:*"> <string key="{local-name()}">{.}</string> </xsl:template> <!-- 处理普通文本节点 --> <xsl:template match="*[not(*)]"> <string key="{local-name()}">{.}</string> </xsl:template> <!-- 处理包含子节点的普通节点 --> <xsl:template match="*[*]"> <map key="{local-name()}"> <xsl:apply-templates select="*"/> </map> </xsl:template> </xsl:stylesheet>
关键修改说明
- 根节点处理:新增根节点模板,将
notifications作为最外层JSON对象的键,解决根节点缺失问题。 - Notification数组强制转换:针对
Notification节点单独编写模板,无论数量多少都包装为数组,满足单个节点也以数组呈现的需求。 - 属性处理:
- 识别
xsi:nil属性,生成带@nil键的对象 - 提取
sObject的xsi:type属性,转为@type键
- 识别
- 命名空间处理:声明输入XML中的所有命名空间,确保节点正确匹配,同时自动去除
sObject子节点的sf:前缀。
内容的提问来源于stack exchange,提问作者techresearch
相关产品推荐
相关产品推荐

