2
votes

I want to convert a user-provided JSON document to its XML representation using the Saxon API, which afterwards serves as the input to an XSLT transformation.

Is there a way I can do this conversion without actually using XQuery/XPath?

I have tried to use JsonToXMLFn to mimic the json-to-xml XPath function without actually writing XSLT or XQuery, but I quickly ran into issues because it (quite logically) needs an XPathContext, and I didn't find an easy way to generate that using the public API.

Currently I use

XQueryExecutable exec = processor.newXQueryCompiler().compile("json-to-xml(.)");
XQueryEvaluator eval = exec.load();
eval.setContextItem(new XdmAtomicValue(jsonString));
XdmDestination destination = new XdmDestination();
eval.run(destination);
return destination.getXdmNode();

Which works fine. But I wonder if there is a way without needing to parse and compile the XQuery expression.

1
I guess using saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/… e.g. processor.newXPathCompiler().evaluateSingle("json-to-xml(.)", new XdmAtomicValue(jsonString)) is a bit less code although still using the compiler to parse the expression.Martin Honnen

1 Answers

1
votes

Perhaps XdmFunctionItem.getSystemFunction(processor, new QName("http://www.w3.org/2005/xpath-functions", "json-to-xml"), 1).call(processor, new XdmAtomicValue(jsonString)) could work but with HE 10.2 it throws an exception "Dynamic functions require Saxon-PE or higher".

I am not sure whether that is still intended given that in XPath expressions HE 10 allows dynamic function calls; the documentation https://saxonica.plan.io/projects/saxon/repository/he/revisions/master/entry/latest10/hej/net/sf/saxon/s9api/XdmFunctionItem.java#L69 says:

 * Get a system function. This can be any function defined in XPath 3.1 functions and operators,
 * including functions in the math, map, and array namespaces. It can also be a Saxon extension
 * function, provided a licensed Processor is used.
 * @return the requested function, or null if there is no such function. Note that some functions
 * (those with particular context dependencies) may be unsuitable for dynamic calling.
 * @throws SaxonApiException if dynamic function calls are not permitted by this Saxon Configuration

and https://saxonica.plan.io/projects/saxon/repository/he/revisions/master/entry/latest10/hej/net/sf/saxon/Configuration.java#L1505 just throws

/**
 * Get a system function. This can be any function defined in XPath 3.1 functions and operators,
 * including functions in the math, map, and array namespaces. It can also be a Saxon extension
 * function, provided a licensed Processor is used.
 *
 * @param name  the name of the required function
 * @param arity the arity of the required function
 * @return the requested function, or null if there is no such function. Note that some functions
 * (those with particular context dependencies) may be unsuitable for dynamic calling.
 * @throws XPathException if dynamic function calls are not permitted by this Saxon Configuration
 */
public Function getSystemFunction(StructuredQName name, int arity) throws XPathException {
    throw new XPathException("Dynamic functions require Saxon-PE or higher");
}

So it seems unless you use PE or EE this is not yet a supported option; with the patch https://saxonica.plan.io/projects/saxon/repository/he/revisions/e5cb4f89b97633000285987b48638a53c6a81b51 or in future Saxon HE 10 or later releases it will work.

With the recent 10.3 release this now works:

    String jsonString = "{ \"number\" : 3.14, \"boolean\": true, \"string\": \"whatever\", \"data\" : [1, 2, 3, 4] }";
    
    XdmValue jsonXml = XdmFunctionItem.getSystemFunction(processor, new QName("http://www.w3.org/2005/xpath-functions", "json-to-xml"), 1).call(processor, new XdmAtomicValue(jsonString));
    
    System.out.println(jsonXml);

gives

<map xmlns="http://www.w3.org/2005/xpath-functions">
   <number key="number">3.14</number>
   <boolean key="boolean">true</boolean>
   <string key="string">whatever</string>
   <array key="data">
      <number>1</number>
      <number>2</number>
      <number>3</number>
      <number>4</number>
   </array>
</map>