|
package saxon.demo;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.xml.transform.dom.DOMSource;
|
|
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Element;
|
|
import org.w3c.dom.Node;
|
|
|
|
import net.sf.saxon.Configuration;
|
|
import net.sf.saxon.dom.NodeOverNodeInfo;
|
|
import net.sf.saxon.event.PipelineConfiguration;
|
|
import net.sf.saxon.expr.XPathContextMajor;
|
|
import net.sf.saxon.expr.instruct.Executable;
|
|
import net.sf.saxon.expr.parser.ExplicitLocation;
|
|
import net.sf.saxon.ma.json.JsonHandlerXML;
|
|
import net.sf.saxon.ma.json.JsonParser;
|
|
import net.sf.saxon.ma.json.JsonReceiver;
|
|
import net.sf.saxon.om.Item;
|
|
import net.sf.saxon.s9api.DocumentBuilder;
|
|
import net.sf.saxon.s9api.Processor;
|
|
import net.sf.saxon.s9api.SaxonApiException;
|
|
import net.sf.saxon.s9api.Serializer;
|
|
import net.sf.saxon.s9api.XdmNode;
|
|
import net.sf.saxon.trans.XPathException;
|
|
import net.sf.saxon.tree.tiny.TinyDocumentImpl;
|
|
|
|
public class JsonXmlRoundTrip_main {
|
|
|
|
public static void main(String[] args) {
|
|
|
|
Logger logger = Logger.getAnonymousLogger();
|
|
|
|
String json = "{\"foo\":\"bar\"}";
|
|
|
|
Element convertedElement = convertJsonToXml(json, logger);
|
|
if (convertedElement == null) {
|
|
return;
|
|
}
|
|
String markup = serializeToString(convertedElement, logger);
|
|
System.out.println("convertedElement:");
|
|
System.out.println(markup);
|
|
|
|
String convertedJson = convertXmlToJson(convertedElement, logger);
|
|
if (convertedJson == null) {
|
|
return;
|
|
}
|
|
System.out.println("convertedJson:");
|
|
System.out.println(convertedJson);
|
|
|
|
System.out.println("compare json:");
|
|
System.out.println(json.equals(convertedJson));
|
|
}
|
|
|
|
private static Element convertJsonToXml(String json, Logger logger) {
|
|
|
|
//adapted from saxon JsonToXMLFn
|
|
|
|
JsonParser parser = new JsonParser();
|
|
int flags = 0;
|
|
// Map<String, Sequence> checkedOptions = null;
|
|
// if (options != null) {
|
|
// checkedOptions = getDetails().optionDetails.processSuppliedOptions(options, context);
|
|
// flags = JsonParser.getFlags(checkedOptions, true, context.getController().getExecutable().isSchemaAware());
|
|
// if ((flags & JsonParser.DUPLICATES_LAST) != 0) {
|
|
// throw new XPathException("json-to-xml: duplicates=use-last is not allowed", "FOJS0005");
|
|
// }
|
|
// if ((flags & JsonParser.DUPLICATES_SPECIFIED) == 0) {
|
|
// if ((flags & JsonParser.VALIDATE) != 0) {
|
|
// flags |= JsonParser.DUPLICATES_REJECTED;
|
|
// } else {
|
|
// flags |= JsonParser.DUPLICATES_RETAINED;
|
|
// }
|
|
// }
|
|
// } else {
|
|
flags = JsonParser.DUPLICATES_RETAINED;
|
|
// }
|
|
|
|
Processor proc = new Processor(false);
|
|
XPathContextMajor context = createDisposableXpathContext(proc);
|
|
String baseUri = null;
|
|
try {
|
|
JsonHandlerXML handler = new JsonHandlerXML(context, baseUri, flags);
|
|
// if (options != null) {
|
|
// handler.setFallbackFunction(checkedOptions, context);
|
|
// }
|
|
parser.parse(json, flags, handler, context);
|
|
Item<?> resultItem = handler.getResult();
|
|
if (resultItem instanceof TinyDocumentImpl) {
|
|
Node domNode = convertTinyDocToDom((TinyDocumentImpl)resultItem);
|
|
if (domNode instanceof Document) {
|
|
return ((Document)domNode).getDocumentElement();
|
|
} else if (domNode instanceof Element) {
|
|
return (Element)domNode;
|
|
}
|
|
}
|
|
logger.severe("unable to convert JSON to XML");
|
|
return null;
|
|
} catch (XPathException e) {
|
|
logger.log(Level.SEVERE, "an error occurred while converting JSON to XML", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static String convertXmlToJson(Node node, Logger logger) {
|
|
|
|
//adapted from saxon XMLToJsonFn
|
|
|
|
Processor proc = new Processor(false);
|
|
XdmNode item = proc.newDocumentBuilder().wrap(node);
|
|
XPathContextMajor context = createDisposableXpathContext(proc);
|
|
PipelineConfiguration pipe = context.getController().makePipelineConfiguration();
|
|
pipe.setXPathContext(context);
|
|
JsonReceiver receiver = new JsonReceiver(pipe);
|
|
receiver.setIndenting(true);
|
|
// if (options.numberFormatter != null) {
|
|
// receiver.setNumberFormatter(options.numberFormatter);
|
|
// }
|
|
// Receiver r = receiver;
|
|
// if (xml.getNodeKind() == Type.DOCUMENT) {
|
|
// r = new DocumentValidator(r, "FOJS0006");
|
|
// }
|
|
|
|
try {
|
|
receiver.open();
|
|
|
|
//this causes IllegalStateException in JsonReceiver
|
|
item.getUnderlyingNode().copy(
|
|
receiver,
|
|
0,
|
|
new ExplicitLocation(null, -1, -1));
|
|
|
|
receiver.close();
|
|
return receiver.getJsonString();
|
|
} catch (XPathException e) {
|
|
logger.log(Level.SEVERE, "unable to convert XML to JSON", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static XPathContextMajor createDisposableXpathContext(Processor proc) {
|
|
|
|
Executable exec = new Executable(proc.getUnderlyingConfiguration());
|
|
return new XPathContextMajor(null, exec);
|
|
}
|
|
|
|
private static String serializeToString(
|
|
Node node,
|
|
Logger logger) {
|
|
|
|
Configuration config = new Configuration();
|
|
Processor processor = new Processor(config);
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
Serializer serializer = processor.newSerializer(baos);
|
|
boolean success = serialize(
|
|
node,
|
|
processor.newDocumentBuilder(),
|
|
serializer,
|
|
logger);
|
|
if (success) {
|
|
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static boolean serialize(
|
|
Node node,
|
|
DocumentBuilder docBldr,
|
|
Serializer serializer,
|
|
Logger logger) {
|
|
|
|
try {
|
|
XdmNode xdmNode = docBldr.build(new DOMSource(node));
|
|
serializer.setOutputProperty(Serializer.Property.INDENT, "yes");
|
|
serializer.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, "yes");
|
|
serializer.setOutputProperty(Serializer.Property.ENCODING, "UTF-8");
|
|
serializer.serializeNode(xdmNode);
|
|
return true;
|
|
} catch (SaxonApiException e) {
|
|
logger.log(Level.SEVERE, "an error occurred while saving data", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static Node convertTinyDocToDom(TinyDocumentImpl tinyDoc) {
|
|
|
|
//saxon no longer implements w3 dom in TinyTree, so NodeOverNodeInfo
|
|
//provides a read-only w3 dom api over tiny node
|
|
//https://www.saxonica.com/documentation9.5/changes/v8.3/external-object-models.html
|
|
return NodeOverNodeInfo.wrap(tinyDoc.getRootNode());
|
|
}
|
|
}
|