如何使用XSLT实现JSON转JSON?解决Saxon HE相关报错
JSON转JSON的XSLT转换问题及解决方案
需求说明
使用XSLT技术将输入JSON转换为指定结构的JSON。
输入JSON
{ "members": [ { "name": "Molecule Man1", "age": 29, "secretIdentity": "Dan Jukes" }, { "name": "Molecule Man2", "age": 30, "secretIdentity": "Martin" } ] }
期望输出JSON
{ "resourceType": "Test", "type": { "name": "Member" }, "displayName": "Molecule Man", "attributes": { "test": [ { "value": "29" } ], "testing": [ { "value": "Dan Jukes" } ] } }
遇到的错误
- SERE0023 JSON序列化:无法处理长度为2的序列
- 尝试使用
<saxon::array>时提示“saxon:array未被识别为Saxon指令,Saxon扩展需要Saxon-PE或更高版本”
现有代码
Java代码
package com.practice; import net.sf.saxon.s9api.*; import javax.xml.transform.stream.StreamSource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class JsonToXsltToJson { public static void main(String[] args) throws FileNotFoundException, SaxonApiException { Processor processor = new Processor(false); TransformJson(processor); } static void TransformJson(Processor processor) throws FileNotFoundException, SaxonApiException { JsonBuilder jsonBuilder = processor.newJsonBuilder(); XdmValue input = jsonBuilder.parseJson(new InputStreamReader(new FileInputStream("input.json"), StandardCharsets.UTF_8)); XsltCompiler xsltCompiler = processor.newXsltCompiler(); XsltExecutable xsltExecutable = xsltCompiler.compile(new StreamSource(new File("jsonTransform.xsl"))); Xslt30Transformer xslt30Transformer = xsltExecutable.load30(); xslt30Transformer.applyTemplates(input, xslt30Transformer.newSerializer(System.out)); } }
原XSLT代码
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:mf="http://example.com/mf" exclude-result-prefixes="#all"> <xsl:param name="nodes" as="map(*)*"/> <xsl:template name="init"> <xsl:apply-templates select="($nodes)"/> </xsl:template> <xsl:template match="."> <xsl:map> <xsl:map-entry key="'resourceType'" select="'Test'"/> <xsl:map-entry key="'type'"> <xsl:map-entry key="'name'" select="'Member'"/> </xsl:map-entry> <xsl:map-entry key="'displayName'" select= "?members?1?name"/> <xsl:map-entry key="'attributes'"> <xsl:map-entry key="'age'"> <xsl:map-entry key="'value'" select="?members?1?age"/> </xsl:map-entry> <xsl:map-entry key="'secretIdentity'"> <xsl:map-entry key="'value'" select="?members?1?secretIdentity"/> </xsl:map-entry> </xsl:map-entry> </xsl:map> </xsl:template> <xsl:output method="json" indent="yes"/> </xsl:stylesheet>
解决方案
错误原因分析
- SERE0023错误:原XSLT中定义的
nodes参数未被正确传入值,模板匹配逻辑导致输出多节点序列,而JSON序列化要求输出单个map或数组。 - saxon:array不支持:
saxon:array是Saxon-PE及以上版本的扩展指令,Saxon HE不兼容,需改用XSLT 3.0标准数组构造语法。
修改后的XSLT代码
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all"> <!-- 输出JSON格式并开启缩进 --> <xsl:output method="json" indent="yes"/> <!-- 指定初始模板为main,避免匹配逻辑歧义 --> <xsl:initial-template name="main"/> <xsl:template name="main"> <!-- 获取第一个成员数据 --> <xsl:variable name="first-member" select="?members?1"/> <!-- 构造目标JSON对应的map结构 --> <xsl:map> <xsl:map-entry key="'resourceType'" select="'Test'"/> <!-- 嵌套map构造type字段 --> <xsl:map-entry key="'type'" select="map { 'name': 'Member' }"/> <xsl:map-entry key="'displayName'" select="'Molecule Man'"/> <xsl:map-entry key="'attributes'"> <xsl:map> <!-- 使用标准数组构造语法array{},将value包装为数组元素 --> <xsl:map-entry key="'test'" select="array{ map { 'value': string($first-member?age) } }"/> <xsl:map-entry key="'testing'" select="array{ map { 'value': $first-member?secretIdentity } }"/> </xsl:map> </xsl:map-entry> </xsl:map> </xsl:template> </xsl:stylesheet>
修改后的Java代码
调整调用逻辑,触发指定初始模板,避免原applyTemplates的匹配问题:
package com.practice; import net.sf.saxon.s9api.*; import javax.xml.transform.stream.StreamSource; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; public class JsonToXsltToJson { public static void main(String[] args) throws FileNotFoundException, SaxonApiException { Processor processor = new Processor(false); TransformJson(processor); } static void TransformJson(Processor processor) throws FileNotFoundException, SaxonApiException { JsonBuilder jsonBuilder = processor.newJsonBuilder(); // 解析输入JSON为根map节点 XdmNode input = jsonBuilder.parseJson(new InputStreamReader(new FileInputStream("input.json"), StandardCharsets.UTF_8)).itemAt(0).asNode(); XsltCompiler xsltCompiler = processor.newXsltCompiler(); XsltExecutable xsltExecutable = xsltCompiler.compile(new StreamSource(new File("jsonTransform.xsl"))); Xslt30Transformer xslt30Transformer = xsltExecutable.load30(); // 设置初始模板的上下文节点为输入JSON根节点 xslt30Transformer.setInitialContextNode(input); // 调用初始模板main生成输出 xslt30Transformer.callTemplate(new QName("main"), xslt30Transformer.newSerializer(System.out)); } }
关键修改点
- XSLT方面:
- 移除未使用的
nodes参数,改用xsl:initial-template指定入口模板,确保输出单个map结构,解决序列长度错误。 - 使用XSLT 3.0标准的
array{...}语法构造数组,替代非标准的saxon:array,兼容Saxon HE。 - 对齐期望输出的字段名(如
test、testing),调整嵌套map结构。
- 移除未使用的
- Java方面:
- 将解析后的XdmValue转为XdmNode,作为初始模板的上下文节点。
- 使用
callTemplate触发指定初始模板,避免applyTemplates的匹配歧义。
内容的提问来源于stack exchange,提问作者Ram Prasad
相关产品推荐
相关产品推荐

