package com.bagri.core.server.api.df.json; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.json.Json; import javax.json.stream.JsonParser; import javax.json.stream.JsonParser.Event; import javax.json.stream.JsonParserFactory; import com.bagri.core.api.BagriException; import com.bagri.core.model.Data; import com.bagri.core.server.api.ContentParser; import com.bagri.core.server.api.ModelManagement; import com.bagri.core.server.api.impl.ContentParserBase; /** * XDM Parser implementation for JSON data format. Uses reference implementation (Glassfish) of json streaming parser. * * @author Denis Sukhoroslov * */ public class JsonpParser extends ContentParserBase implements ContentParser<String> { private JsonParserFactory factory = Json.createParserFactory(null); /** * * @param model the model management component * @param json the document content in JSON format * @return the list of parsed XDM data elements * @throws IOException in case of content read exception * @throws BagriException in case of content parse exception */ public static List<Data> parseDocument(ModelManagement model, String json) throws IOException, BagriException { JsonpParser parser = new JsonpParser(model); return parser.parse(json); } /** * * @param model the model management component */ JsonpParser(ModelManagement model) { super(model); } /** * {@inheritDoc} * */ @Override public void init(Properties properties) { // process/convert any specific properties here Map<String, Object> params = new HashMap<>(); for (Map.Entry prop: properties.entrySet()) { //String name = (String) prop.getKey(); params.put((String) prop.getKey(), prop.getValue()); } factory = Json.createParserFactory(params); } /** * {@inheritDoc} */ @Override public List<Data> parse(String json) throws BagriException { try (Reader reader = new StringReader(json)) { return parse(reader); } catch (IOException ex) { throw new BagriException(ex, BagriException.ecInOut); } } /** * {@inheritDoc} */ @Override public List<Data> parse(File file) throws BagriException { try (Reader reader = new FileReader(file)) { return parse(reader); } catch (IOException ex) { throw new BagriException(ex, BagriException.ecInOut); } } /** * {@inheritDoc} */ @Override public List<Data> parse(InputStream stream) throws BagriException { try (JsonParser parser = factory.createParser(stream)) { return parse(parser); } } /** * {@inheritDoc} */ @Override public List<Data> parse(Reader reader) throws BagriException { try (JsonParser parser = factory.createParser(reader)) { return parse(parser); } } /** * * @param parser the JSON streaming parser * @return the list of parsed XDM data elements * @throws BagriException in case of any parsing error */ public List<Data> parse(JsonParser parser) throws BagriException { ParserContext ctx = initContext(); while (parser.hasNext()) { processEvent(ctx, parser); } return ctx.getDataList(); } private void processEvent(ParserContext ctx, JsonParser parser) throws BagriException { //, XMLStreamException { JsonParser.Event event = parser.next(); if (event == Event.VALUE_STRING || event == Event.VALUE_NUMBER) { logger.trace("processEvent; got token: {}; value: {}", event.name(), parser.getString()); } else if (event == Event.KEY_NAME) { logger.trace("processEvent; got token: {}; key: {}", event.name(), parser.getString()); } else { logger.trace("processEvent; got token: {}", event.name()); } switch (event) { case START_OBJECT: if (ctx.getTopData() == null) { ctx.addDocument("/"); } else { ctx.addElement(); } break; case START_ARRAY: ctx.addArray(); break; case KEY_NAME: ctx.addData(parser.getString()); break; case END_ARRAY: case END_OBJECT: ctx.endElement(); break; case VALUE_FALSE: ctx.addValue(false); break; case VALUE_TRUE: ctx.addValue(true); break; case VALUE_NULL: ctx.addValue(); break; case VALUE_NUMBER: if (parser.isIntegralNumber()) { ctx.addValue(parser.getLong()); } else { ctx.addValue(parser.getBigDecimal()); } break; case VALUE_STRING: ctx.addValue(parser.getString()); break; default: logger.trace("processEvent; unknown event: {}", event); } } } /* private Data getTopData(ParserContext ctx) { //for (int i = ctx.getStackSize() - 1; i >= 0; i--) { for (int i = 0; i < ctx.getStackSize(); i++) { Data data = ctx.getStackElement(i); logger.trace("getTopData; index: {}; data: {}", i, data); if (data != null && data.getElement() != null) { return data; } } return null; } private void processDocument(ParserContext ctx, String name) throws BagriException { String root = "/" + (name == null ? "" : name); ctx.setDocType(model.translateDocumentType(root)); Path path = model.translatePath(ctx.getDocType(), "", NodeKind.document, XQItemType.XQBASETYPE_ANYTYPE, Occurrence.onlyOne); Element start = new Element(); Data data = new Data(path, start); ctx.addStack(data); ctx.addData(data); } private boolean isAttribute(String name) { return name.startsWith("-") || name.startsWith("@"); } private void processStartElement(ParserContext ctx, boolean isArray) { if (isArray) { ctx.addStack(null); } else { Data current = ctx.lastData(); if (current == null || current.getNodeKind() != NodeKind.element) { ctx.addStack(null); } } } private void processStartElement(ParserContext ctx, String name) throws BagriException { Data parent = getTopData(ctx); if (!name.equals(parent.getName())) { Data current = null; if (isAttribute(name)) { name = name.substring(1); if (name.startsWith("xmlns")) { current = addData(ctx, parent, NodeKind.namespace, "/#" + name, null, XQItemType.XQBASETYPE_STRING, Occurrence.zeroOrOne); } else { current = addData(ctx, parent, NodeKind.attribute, "/@" + name, null, XQItemType.XQBASETYPE_ANYATOMICTYPE, Occurrence.zeroOrOne); } } else if (name.equals("#text")) { current = new Data(null, null); } else { current = addData(ctx, parent, NodeKind.element, "/" + name, null, XQItemType.XQBASETYPE_ANYTYPE, Occurrence.zeroOrOne); } ctx.addStack(current); } } private void processEndElement(ParserContext ctx) { if (ctx.getStackSize() > 0) { Data current = ctx.popData(); logger.trace("processEndElement; got current: {}", current); adjustParent(current); } } private void processValueElement(ParserContext ctx, String value) throws BagriException { Data current = ctx.popData(); boolean isArray = current == null; if (isArray || current.getElement() == null) { current = getTopData(ctx); } if (current.getNodeKind() == NodeKind.element) { addData(ctx, current, NodeKind.text, "/text()", value, XQItemType.XQBASETYPE_ANYATOMICTYPE, isArray ? Occurrence.zeroOrMany : Occurrence.zeroOrOne); } else { current.getElement().setValue(value); // do we need set position here?? } if (isArray) { ctx.addStack(null); } } */