/** * $Id: $ * $Date: $ * */ package org.xmlsh.json; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.List; import javanet.staxutils.OutputFactory; import javax.xml.crypto.dsig.TransformException; import javax.xml.namespace.QName; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import javax.xml.transform.stream.StreamSource; 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.WhitespaceStrippingPolicy; import net.sf.saxon.s9api.XdmNode; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.builtin.commands.exit; import org.xmlsh.core.CoreException; import org.xmlsh.core.XValue; import org.xmlsh.sh.shell.SerializeOpts; import org.xmlsh.sh.shell.Shell; import org.xmlsh.sh.shell.ShellConstants; import org.xmlsh.util.Util; import org.xmlsh.util.XMLUtils; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.json.JsonWriteContext; public class JXONConverter extends JXConverter { static final String kJXML_URI = "http://www.xmlsh.org/jxml"; static final QName kATTR_ENCODING = new QName("encoding"); static final QName kATTR_NAME = new QName("name"); static final QName kATTR_VALUE = new QName("value"); static final QName kATTR_UNWRAP = new QName("unwrap"); static final QName kATTR_HTML = new QName("html"); // A String formated as XHTML static final QName kELEM_XJSON = new QName(kJXML_URI, "xjson"); static final QName kELEM_FILE = new QName(kJXML_URI, "file"); static final QName kELEM_OBJECT = new QName(kJXML_URI, "object"); // A JSON Object static final QName kELEM_MEMBER = new QName(kJXML_URI, "member"); // A JSON Object Member static final QName kELEM_STRING = new QName(kJXML_URI, "string"); // A JSON STRING static final QName kELEM_NUMBER = new QName(kJXML_URI, "number"); // A JSON NUMBER static final QName kELEM_ARRAY = new QName(kJXML_URI, "array"); // A JSON ARRAY static final QName kELEM_BOOLEAN = new QName(kJXML_URI, "boolean"); // A JSON Literal (true,false) static final QName kELEM_NULL = new QName(kJXML_URI, "null"); // A JSON Literal null public static Logger mLogger = LogManager.getLogger(); class JConverter extends JSONConverter { protected JConverter(XMLStreamReader reader, OutputStream os) throws ConverterException { super(reader, os); } @Override public boolean startElement(StartElement start, QName name) throws ConverterException { mLogger.entry(start, name); try { if(name.equals(kELEM_XJSON)) { while (parse()) ; return mLogger.exit( false); } else if(name.equals(kELEM_FILE)) throw new ConverterException("Depreciated element not supported: " + kELEM_FILE.toString()); else if(name.equals(kELEM_OBJECT)) writeObject(start); else if(name.equals(kELEM_ARRAY)) writeArray(start); else if(name.equals(kELEM_MEMBER)) writeMember(start); else if(name.equals(kELEM_NUMBER)) writeNumber(start); else if(name.equals(kELEM_BOOLEAN)) writeBoolean(start); else if(name.equals(kELEM_NULL)) writeNull(); else if(name.equals(kELEM_STRING)) writeString(start); else throw new ConverterException("Depreciated element not supported: " + kELEM_FILE.toString()); } catch (ConverterException e) { throw e; } catch (Exception e) { Util.wrapException(e, ConverterException.class); } return mLogger.exit(true ); } private void writeString(StartElement start) throws XMLStreamException, UnsupportedEncodingException, FileNotFoundException, IOException, TransformException, SaxonApiException, CoreException { mLogger.entry(start ); String value = getAttr(start, kATTR_VALUE); @SuppressWarnings("unused") String encoding = getAttr(start, kATTR_ENCODING); String unwrap = getAttr(start, kATTR_UNWRAP); String html = getAttr(start, kATTR_HTML); String chars; if(value != null) chars = value; else { // readString eats the close tag chars = readString(Util.parseBoolean(html)); } // If Unwrap then trim off <html> and leading and trailing blanks if(Util.parseBoolean(unwrap)) { chars = unwrap(chars); } mGenerator.writeString(chars); mLogger.trace("writeString({}) leaving. prevLevel: {} level: {} " , chars ); mLogger.exit( ); } /* * Parse an HTML element as XML and reserialize as HTML, store as a JSON * string */ private String readString(boolean bHTML) throws TransformException, XMLStreamException, SaxonApiException, IOException { mLogger.entry(bHTML); byte[] bytes = bHTML ? serializeAsXML() : serializeAsString(); // String xs = new String(xhtml,klENCODING_UTF_8); if(bHTML) return mLogger.exit( formatAsHtml(bytes)); else return mLogger.exit(new String(bytes, ShellConstants.kENCODING_UTF_8)); } /* * Unwrap a string by 1) Remove leading and trailing blanks 2) Remove * any <html> (any case) from beginning and end 3) Remove leading and * trailing blanks from the result */ private String unwrap(String value) { value = value.trim(); if( value.length() < 6 || ! "<html>".equalsIgnoreCase(value.substring(0, 6))) return value ; value = value.substring(6); if(value.length() < 7 ||! "</html>".equalsIgnoreCase(value.substring(value.length() - 7))) return value ; value = value.substring(0, value.length() - 7); return value.trim(); } private String getAttr(StartElement start, QName attr) { Attribute a = start.getAttributeByName(attr); if(a == null) return null; return a.getValue(); } private void writeNull() throws IOException, ConverterException { mLogger.entry( ); mGenerator.writeNull(); mLogger.exit( ); } private void writeBoolean(StartElement start) throws XMLStreamException, IOException, ConverterException { mLogger.entry(start); String chars; Attribute v = start.getAttributeByName(kATTR_VALUE); if(v != null) chars = v.getValue(); else { chars = readString(); } chars = chars.trim(); mGenerator.writeBoolean(Util.parseBoolean(chars)); mLogger.exit( ); } private void writeNumber(StartElement start) throws ConverterException, IOException, XMLStreamException { mLogger.entry(start); String chars; Attribute v = start.getAttributeByName(kATTR_VALUE); if(v != null) chars = v.getValue(); else { chars = readString(); } chars = chars.trim(); // Number num = NumberFormat.getInstance().parse(chars); mGenerator.writeNumber(chars); mLogger.exit(); } private void writeMember(StartElement start) throws IOException, ConverterException { mLogger.entry(start); String name = start.getAttributeByName(kATTR_NAME).getValue(); mGenerator.writeFieldName(name); parse(); mLogger.exit(); } private void writeArray(StartElement start) throws IOException, ConverterException { mLogger.entry( start ); mGenerator.writeStartArray(); do { if(!parse()) break; } while (true); mGenerator.writeEndArray(); mLogger.exit(); } private void writeObject(StartElement start) throws IOException, ConverterException { mLogger.entry( start ); mGenerator.writeStartObject(); do { if(!parse()) break; } while (true); mGenerator.writeEndObject(); mLogger.exit(); } /* * Serialize the body as HTML and return as a string */ private String formatAsHtml(byte[] xhtml) throws SaxonApiException, IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Serializer ser = Shell.getProcessor().newSerializer(); ser.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, "yes"); ser.setOutputProperty(Serializer.Property.INDENT, "no"); ser.setOutputProperty(Serializer.Property.METHOD, "html"); ser.setOutputProperty(Serializer.Property.ENCODING, ShellConstants.kENCODING_UTF_8); ser.setOutputStream(bos); Processor processor = Shell.getProcessor(); DocumentBuilder builder = processor.newDocumentBuilder(); builder.setWhitespaceStrippingPolicy(WhitespaceStrippingPolicy.ALL); XdmNode node = builder.build(new StreamSource(new ByteArrayInputStream(xhtml))); processor.writeXdmValue(node, ser); return bos.toString(ShellConstants.kENCODING_UTF_8).trim(); } /* * Serialize as XML */ private byte[] serializeAsXML() throws XMLStreamException, UnsupportedEncodingException, FactoryConfigurationError, IOException { mLogger.entry( ); XMLEventWriter writer = null; XMLEventReader reader = null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); XMLOutputFactory ofact = new OutputFactory(); writer = ofact.createXMLEventWriter(bos, ShellConstants.kENCODING_UTF_8) ; reader = XMLUtils.createEventReader( readString() ); writer.add(reader); writer.flush(); return mLogger.exit( bos.toByteArray() ); } finally { Util.safeClose(reader); Util.safeClose(writer); } } private byte[] serializeAsString() throws XMLStreamException, UnsupportedEncodingException, IOException { String s = readString(); return s.getBytes(ShellConstants.kENCODING_UTF_8); } @Override protected boolean startDocument(XMLEvent e) throws ConverterException { mLogger.entry( e ); return mLogger.exit(true ); } @Override protected boolean endElement(EndElement e ) throws ConverterException { mLogger.entry( e ); return mLogger.exit(false ); } @Override protected boolean endDocument(XMLEvent e) throws ConverterException { mLogger.entry( e); return mLogger.exit(false ); } @Override protected boolean characters(XMLEvent e) { // Ignore unexpected chars return mLogger.exit(true ); } } class XConverter extends XMLConverter { protected XConverter(InputStream is, XMLStreamWriter sw) throws ConverterException { super(is, sw); } // Write a value and @Override void writeArray() throws ConverterException { mLogger.entry( ); try { JsonToken tok; writeStartElement( kELEM_ARRAY ); while ((tok = nextToken()) != null && tok != JsonToken.END_ARRAY) { writeValue(tok); } writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } @Override void writeObject() throws ConverterException { mLogger.entry( ); JsonToken tok; try { writeStartElement( kELEM_OBJECT ); while ((tok = nextToken()) == JsonToken.FIELD_NAME) writeMember(); if(tok == null) throw new ConverterException("Unexpected EOF"); if(tok != JsonToken.END_OBJECT) throw new ConverterException("Unexpected token: " + tok); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } private void writeMember() throws ConverterException { mLogger.entry( ); try { writeStartElement( kELEM_MEMBER ); String name = mParser.getCurrentName(); writeAttribute( kATTR_NAME , name ); writeValue(nextToken()); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } catch (IOException e) { throw new ConverterException(e); } } @Override void writeBoolean(boolean value) throws ConverterException { mLogger.entry(value); try { writeStartElement( kELEM_BOOLEAN ); writeCharacters(value ? "true" : "false"); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } @Override void writeNull() throws ConverterException { mLogger.entry( ); try { writeStartElement(kELEM_NULL); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } @Override void writeNumber() throws ConverterException { mLogger.entry( ); try { writeStartElement(kELEM_NUMBER); writeCharacters( getStringValue() ); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } @Override void writeString(String s) throws ConverterException { mLogger.entry(s); try { writeStartElement(kELEM_STRING); writeCharacters(s); writeEndElement(); } catch (XMLStreamException e) { throw new ConverterException(e); } } } public JXONConverter( SerializeOpts serializeOpts, List<XValue> mArgs) { super(serializeOpts, mArgs); } @Override JSONConverter newJConverter(XMLStreamReader reader, OutputStream os) throws ConverterException { return new JConverter(reader, os); } @Override XMLConverter newXMLConverter(InputStream is, XMLStreamWriter sw) throws ConverterException { return new XConverter(is, sw); } } /* * Copyright (C) 2008-2012 David A. Lee. * * The contents of this file are subject to the "Simplified BSD License" (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/bsd-license.php * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is: all this file. * * The Initial Developer of the Original Code is David A. Lee * * Portions created by (your name) are Copyright (C) (your legal entity). All * Rights Reserved. * * Contributor(s): David A. Lee */