如何在Java中使用Saxon为XQuery配置多输入文件参数?
解决Java调用XQuery处理JSON的报错与多文件支持问题
咱们先搞定那个烦人的XML解析报错,再一步步实现多JSON文件的处理支持:
一、修复SXXP0003报错:别把JSON当XML解析
你遇到的SXXP0003: Error reported by XML parser: Content is not allowed in prolog错误,根源是你把JSON输入流当成XML文档来解析了。JSON不是XML格式,XML解析器看到开头的{就会认为序言(XML文档开头的声明部分)有非法内容,直接报错。
正确的做法是:直接把JSON解析成Saxon支持的XDM(XQuery Data Model)对象,作为外部变量传给XQuery,而不是转换成XML节点。
二、修改XQuery脚本,支持外部参数与多文件输入
我们需要把原来硬编码的JSON文件路径改成接收外部变量,同时支持处理多个JSON对象。修改后的XQuery.xq如下:
xquery version "3.1"; declare namespace array = "http://www.w3.org/2005/xpath-functions/array"; declare namespace map = "http://www.w3.org/2005/xpath-functions/map"; declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization"; declare option output:method "json"; declare option output:indent "yes"; declare variable $input-json as item()* external; (: 接收多个JSON对象的序列 :) array { for $j in $input-json (: 遍历每个输入的JSON对象 :) where ("Fred" = $j?FirstName or 30 != $j?Age) return array { for $i in (1 to $j?Phone => array:size()) let $e := $j?Phone($i) where ($e?type = "home" or fn:matches($e?type, "^mob.*$")) return map { "Name (First)": data($j?FirstName), "Name (Last)": data($j?Surname), "age": data($j?Age), "Phone": data($e?number), "ConstantValue": "TEST" } } }
关键改动点:
- 新增
declare variable $input-json as item()* external;:声明一个外部变量,接收多个JSON对象的序列 - 外层新增
for $j in $input-json循环:遍历每个输入的JSON文件内容 - 保留了你原来的过滤和转换逻辑,确保功能不变
三、修改Java代码,正确传递JSON参数并支持多文件
现在调整Java代码,把每个JSON文件解析成XDM对象,打包成序列传给XQuery,同时去掉错误的XML解析逻辑:
import net.sf.saxon.Configuration; import net.sf.saxon.s9api.*; import org.junit.Test; import org.springframework.util.StreamUtils; import java.io.*; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; public class XQueryTest { @Test public void testMultiJsonXQuery() throws Exception { // 支持传入多个JSON文件路径 runXQuery(List.of("/1.json", "/2.json"), "/XQuery.xq", "/Multi_Output.json"); } private void runXQuery(List<String> dataFiles, String xQueryFile, String expectedOutput) throws Exception { // 解析所有输入JSON文件为XDM对象 List<XdmItem> inputItems = new ArrayList<>(); Processor processor = new Processor(false); JsonParser jsonParser = processor.newJsonParser(); for (String dataFile : dataFiles) { try (InputStream dataStream = getClass().getResourceAsStream(dataFile)) { XdmValue jsonValue = jsonParser.parse(new InputSource(dataStream)); inputItems.addAll(jsonValue.asList()); } } // 读取XQuery脚本 try (InputStream xQueryStream = getClass().getResourceAsStream(xQueryFile)) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); run(processor, inputItems, xQueryStream, outputStream); // 验证输出 String json = outputStream.toString(); String expected = StreamUtils.copyToString( getClass().getResourceAsStream(expectedOutput), Charset.defaultCharset() ); assertEquals(expected, json); } } private static void run(Processor processor, List<XdmItem> inputItems, InputStream queryStream, OutputStream output) throws SaxonApiException { XQueryCompiler compiler = processor.newXQueryCompiler(); XQueryExecutable executor = compiler.compile(new InputSource(queryStream)); XQueryEvaluator evaluator = executor.load(); // 设置外部变量:把多个JSON对象序列传给$input-json QName inputQName = new QName("input-json"); evaluator.setExternalVariable(inputQName, new XdmSequence(inputItems)); // 配置JSON序列化 Serializer serializer = processor.newSerializer(output); serializer.setOutputProperty(Serializer.Property.METHOD, "json"); serializer.setOutputProperty(Serializer.Property.INDENT, "yes"); evaluator.run(serializer); } }
关键改动点:
- 用Saxon的JsonParser解析JSON:避免了XML解析的错误,直接把JSON转换成XDM对象(符合XQuery的数据模型)
- 支持多文件输入:通过
List<String> dataFiles接收多个JSON文件路径,逐个解析后加入序列 - 正确传递外部变量:用
XdmSequence把多个JSON对象打包,传给XQuery中声明的$input-json变量 - 移除错误的XML节点处理:删掉了
SAXSource、DocumentBuilder相关的代码,不再把JSON当XML处理
四、测试与验证
- 把
2.json放到资源目录下,格式和1.json类似即可 - 运行
testMultiJsonXQuery方法,会同时处理1.json和2.json,输出合并后的JSON结果
这样既解决了原来的XML解析报错,又实现了多JSON文件的处理需求~
内容的提问来源于stack exchange,提问作者Roeland Van Heddegem
相关产品推荐
相关产品推荐

