问题
In my Java code I am trying to create a Saxon document (DOM) that is the contents of a JSON file. This should be possible but the code I have fails.
The full code for this is at SaxonQuestions.zip, TestLoadJson.java and is also listed below. In this code the evaluate() fails.
TestLoadJson.java
import net.sf.saxon.Configuration;
import net.sf.saxon.s9api.*;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import javax.xml.transform.sax.SAXSource;
import java.io.*;
import java.nio.charset.Charset;
public class TestLoadJson {
public static void main(String[] args) throws Exception {
// get the file
File jsonFile = new File("files", "SouthWind.json");
Charset inputCharset = Charset.forName("UTF-8");
FileInputStream fis = new FileInputStream(jsonFile);
InputStreamReader isr = new InputStreamReader(fis, inputCharset);
BufferedReader br = new BufferedReader(isr);
String str;
StringBuilder buf = new StringBuilder();
while ((str = br.readLine()) != null)
buf.append(str).append('\n');
br.close();
isr.close();
fis.close();
// set up the compiler
Configuration config = XmlDatasource.createEnterpriseConfiguration();
Processor processor = new Processor(config);
XPathCompiler xPathCompiler = processor.newXPathCompiler();
// need an XML document
DocumentBuilder doc_builder = processor.newDocumentBuilder();
XMLReader reader = XmlDatasource.createXMLReader();
InputSource xmlSource = new InputSource(new ByteArrayInputStream("<root/>".getBytes()));
SAXSource saxSource = new SAXSource(reader, xmlSource);
XdmNode xmlRootNode = doc_builder.build(saxSource);
// give it the JSON
buf.insert(0, "parse-json(");
buf.append(")");
Object json = xPathCompiler.evaluate(buf.toString(), xmlRootNode);
System.out.println("JSON read in!!! json = " + json);
}
}
回答1:
If you have a Java String
with JSON pass it in as a variable to XPath and call parse-json
on the variable:
Processor processor = new Processor(true);
String[] jsonExamples = { "1", "true", "null", "\"string\"", "[1,2,3]", "{ \"prop\" : \"value\" }" };
XPathCompiler compiler = processor.newXPathCompiler();
compiler.declareVariable(new QName("json"));
XPathExecutable executable = compiler.compile("parse-json($json)");
XPathSelector selector = executable.load();
for (String json : jsonExamples) {
selector.setVariable(new QName("json"), new XdmAtomicValue(json));
XdmValue value = selector.evaluate();
System.out.println(value);
}
If you have a file with JSON pass its file name or in general URI as a variable to XPath and call json-doc
(https://www.w3.org/TR/xpath-functions/#func-json-doc) on the variable:
compiler = processor.newXPathCompiler();
compiler.declareVariable(new QName("json-uri"));
executable = compiler.compile("json-doc($json-uri)");
selector = executable.load();
selector.setVariable(new QName("json-uri"), new XdmAtomicValue("example1.json")); // pass in a relative (e.g. 'example.json' or 'subdir/example.json') or an absolute URI (e.g. 'file:///C:/dir/subdir/example.json' or 'http://example.com/example.json') here, not an OS specific file path
XdmValue value = selector.evaluate();
System.out.println(value);
Of course you can separate the steps and parse a string to an XdmValue or a file to an XdmValue and then pass it in later as a variable to another XPath evaluation.
So lets assume you have employees.json
containing
{
"employees": [
{
"name": "mike",
"department": "accounting",
"age": 34
},
{
"name": "sally",
"department": "sales",
"age": 24
}
]
}
then you can parse it with the second sample into an XdmValue value and use that further as a context item for an expression e.g
avg(?employees?*?age)
would compute the average age:
Processor processor = new Processor(true);
XPathCompiler compiler = processor.newXPathCompiler();
compiler.declareVariable(new QName("json-uri"));
XPathExecutable executable = compiler.compile("json-doc($json-uri)");
XPathSelector selector = executable.load();
selector.setVariable(new QName("json-uri"), new XdmAtomicValue("employees.json"));
XdmValue value = selector.evaluate();
System.out.println(value);
executable = compiler.compile("avg(?employees?*?age)");
selector = executable.load();
selector.setContextItem((XdmItem) value);
XdmItem result = selector.evaluateSingle();
System.out.println(result);
At https://xqueryfiddle.liberty-development.net/94hwphZ I have another sample processing JSON, it also computes the average of a value with an expression using the lookup operator ?
, first with ?Students
to select the Students
item of the context map, then with an asterisk ?*
on the returned array to get a sequence of all array items, finally with ?Grade
to select the Grade
value of each array item:
avg(?Students?*!(?Grade, 70)[1])
but with the additional requirement to select a default of 70
for those objects/maps that don't have a Grade
. The sample JSON is
{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
The fiddle supports XQuery 3.1 but like for XPath 3.1 the JSON is passed in as a variable and then parsed with parse-json
into an XDM item to serve as the context item for further evaluation.
To give some examples of more complex XPath 3.1 expressions against JSON I have taken the JSON sample from the path examples in https://github.com/json-path/JsonPath as the JSON input to parse-json
(if you have a string) or json-doc
if you have a URI to a file or even a HTTP(S) location and used it as the context item for some paths (evaluated in the fiddle as XQuery 3.1 but XPath 3.1 is a subset and I think I have restricted the samples to XPath 3.1:
The samples are at:
- https://xqueryfiddle.liberty-development.net/gWmuPs6/0 :
?store?book?*?author
: "the authors of all books" - https://xqueryfiddle.liberty-development.net/gWmuPs6/1 :
?store?*
: "all things in the store, both books and bicycles" - https://xqueryfiddle.liberty-development.net/gWmuPs6/2 :
?store?book?3
: "the third book" - https://xqueryfiddle.liberty-development.net/gWmuPs6/3 :
?store?book?(1,2)
: "the first two books" - https://xqueryfiddle.liberty-development.net/gWmuPs6/4 :
?store?book?*[?isbn]
: "all books with an isbn number" - https://xqueryfiddle.liberty-development.net/gWmuPs6/5 :
?store?book?*[?price < 10]
: "all books with a price less than 10" - https://xqueryfiddle.liberty-development.net/gWmuPs6/6 :
let $context := . return ?store?book?*[?price <= $context?expensive]
: "all books with a price less than expensive" - https://xqueryfiddle.liberty-development.net/gWmuPs6/7 :
count(?store?book?*)
: "the number of books"
The file is
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
来源:https://stackoverflow.com/questions/63100636/how-do-i-load-a-json-file-into-the-dom-in-saxon-running-in-java