/* * Copyright 2011, 2012 Odysseus Software GmbH * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.synapse.commons.staxon.core.json; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.Arrays; import javax.xml.namespace.QName; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.synapse.commons.staxon.core.base.AbstractXMLOutputFactory; import org.apache.synapse.commons.staxon.core.event.SimpleXMLEventWriter; import org.apache.synapse.commons.staxon.core.json.stream.JsonStreamFactory; import org.apache.synapse.commons.staxon.core.json.stream.JsonStreamTarget; import org.apache.synapse.commons.staxon.core.json.stream.util.RemoveRootTarget; import org.apache.synapse.commons.staxon.core.json.stream.util.CustomRegexMatchReplaceIgnoreAutoPrimitiveTarget; import org.apache.synapse.commons.staxon.core.json.stream.util.CustomRegexIgnoreAutoPrimitiveTarget; import org.apache.synapse.commons.staxon.core.json.stream.util.AutoArrayTarget; import org.apache.synapse.commons.staxon.core.json.stream.util.AutoPrimitiveTarget; /** * XML output factory for streaming to JSON. */ public class JsonXMLOutputFactory extends AbstractXMLOutputFactory { /** * <p>Start/end arrays automatically?</p> * <p/> * <p>The default value is <code>false</code>.</p> */ public static final String PROP_AUTO_ARRAY = "JsonXMLOutputFactory.autoArray"; /** * <p>Convert element text to JSON primitives (number, boolean, null) automatically?</p> * <p/> * <p>The default value is <code>false</code>.</p> */ public static final String PROP_AUTO_PRIMITIVE = "JsonXMLOutputFactory.autoPrimitive"; /** * <p>Whether to use the {@link JsonXMLStreamConstants#MULTIPLE_PI_TARGET} * processing instruction target to trigger an array start. * If <code>true</code>, a PI is used to inform the writer to begin an array, * passing the name of following multiple elements as data. * The writer will close arrays automatically.</p> * <p/> * <p>Note that the element given in the PI may be written zero times, * indicating an empty array.</p> * <p/> * <p>The default value is true.</p> */ public static final String PROP_MULTIPLE_PI = "JsonXMLOutputFactory.multiplePI"; /** * <p>JSON documents may have have multiple root properties. However, * XML requires a single root element. This property takes the name * of a "virtual" root element, which will be removed from the stream * when writing.</p> * <p/> * <p>The default value is <code>null</code>.</p> */ public static final String PROP_VIRTUAL_ROOT = "JsonXMLOutputFactory.virtualRoot"; /** * <p>Namespace prefix separator.</p> * <p/> * <p>The default value is <code>':'</code>.</p> */ public static final String PROP_NAMESPACE_SEPARATOR = "JsonXMLOutputFactory.namespaceSeparator"; /** * <p>Whether to write namespace declarations.</p> * <p/> * <p>The default value is <code>true</code>.</p> */ public static final String PROP_NAMESPACE_DECLARATIONS = "JsonXMLOutputFactory.namespaceDeclarations"; /** * <p>Format output for better readability?</p> * <p/> * <p>The default value is <code>false</code>.</p> */ public static final String PROP_PRETTY_PRINT = "JsonXMLOutputFactory.prettyPrint"; /** * <p>Convert element text to JSON primitives with ignore given regex (number, boolean, null) automatically?</p> * * <p>The default value is <code>null</code>.</p> */ public static final String PROP_CUSTOM_REGEX = "JsonXMLOutputFactory.customRegex"; public static final String PROP_CUSTOM_REPLACE_REGEX = "JsonXMLOutputFactory.customReplaceRegex"; public static final String PROP_CUSTOM_REPLACE_SEQUENCE = "JsonXMLOutputFactory.customReplaceSequence"; private JsonStreamFactory streamFactory; private boolean multiplePI; private QName virtualRoot; private boolean autoArray; private boolean autoPrimitive; private boolean prettyPrint; private char namespaceSeparator; private boolean namespaceDeclarations; private String customRegex; private String customReplaceRegex; private String customReplaceSequence; public JsonXMLOutputFactory() throws FactoryConfigurationError { this(JsonXMLConfig.DEFAULT); } public JsonXMLOutputFactory(JsonStreamFactory streamFactory) { this(JsonXMLConfig.DEFAULT, streamFactory); } public JsonXMLOutputFactory(JsonXMLConfig config) throws FactoryConfigurationError { this(config, JsonStreamFactory.newFactory()); } public JsonXMLOutputFactory(JsonXMLConfig config, JsonStreamFactory streamFactory) { this.multiplePI = config.isMultiplePI(); this.virtualRoot = config.getVirtualRoot(); this.autoArray = config.isAutoArray(); this.autoPrimitive = config.isAutoPrimitive(); this.prettyPrint = config.isPrettyPrint(); this.namespaceSeparator = config.getNamespaceSeparator(); this.namespaceDeclarations = config.isNamespaceDeclarations(); this.streamFactory = streamFactory; this.customRegex= config.getCustomRegex(); this.customReplaceRegex = config.getCustomReplaceRegex(); this.customReplaceSequence = config.getCustomReplaceSequence(); /* * initialize standard properties */ super.setProperty(IS_REPAIRING_NAMESPACES, config.isRepairingNamespaces()); } private JsonStreamTarget decorate(JsonStreamTarget target) { if (virtualRoot != null) { target = new RemoveRootTarget(target, virtualRoot, namespaceSeparator); } if (autoArray) { target = new AutoArrayTarget(target); } if (autoPrimitive) { if (customRegex != null) { target = new CustomRegexIgnoreAutoPrimitiveTarget(target, false, customRegex); } else if (customReplaceRegex != null) { target = new CustomRegexMatchReplaceIgnoreAutoPrimitiveTarget(target, false, customReplaceRegex, customReplaceSequence); } else { target = new AutoPrimitiveTarget(target, false); } } return target; } @Override public JsonXMLStreamWriter createXMLStreamWriter(OutputStream stream, String encoding) throws XMLStreamException { try { return createXMLStreamWriter(new OutputStreamWriter(stream, encoding)); } catch (UnsupportedEncodingException e) { throw new XMLStreamException(e); } } @Override public JsonXMLStreamWriter createXMLStreamWriter(Writer stream) throws XMLStreamException { boolean repairNamespaces = Boolean.TRUE.equals(getProperty(IS_REPAIRING_NAMESPACES)); try { return new JsonXMLStreamWriter(decorate(streamFactory.createJsonStreamTarget(stream, prettyPrint)), repairNamespaces, multiplePI, namespaceSeparator, namespaceDeclarations); } catch (IOException e) { throw new XMLStreamException(e); } } @Override public JsonXMLStreamWriter createXMLStreamWriter(OutputStream stream) throws XMLStreamException { boolean repairNamespaces = Boolean.TRUE.equals(getProperty(IS_REPAIRING_NAMESPACES)); try { return new JsonXMLStreamWriter(decorate(streamFactory.createJsonStreamTarget(stream, prettyPrint)), repairNamespaces, multiplePI, namespaceSeparator, namespaceDeclarations); } catch (IOException e) { throw new XMLStreamException(e); } } @Override public XMLEventWriter createXMLEventWriter(XMLStreamWriter writer) throws XMLStreamException { return new SimpleXMLEventWriter(writer); } @Override public boolean isPropertySupported(String name) { return super.isPropertySupported(name) || Arrays.asList(PROP_AUTO_ARRAY, PROP_MULTIPLE_PI, PROP_VIRTUAL_ROOT, PROP_NAMESPACE_SEPARATOR, PROP_NAMESPACE_DECLARATIONS, PROP_PRETTY_PRINT, PROP_CUSTOM_REPLACE_REGEX, PROP_CUSTOM_REPLACE_SEQUENCE).contains(name); } @Override public Object getProperty(String name) throws IllegalArgumentException { if (super.isPropertySupported(name)) { return super.getProperty(name); } else { // proprietary properties if (PROP_AUTO_ARRAY.equals(name)) { return Boolean.valueOf(autoArray); } else if (PROP_AUTO_PRIMITIVE.equals(name)) { return Boolean.valueOf(autoPrimitive); } else if (PROP_MULTIPLE_PI.equals(name)) { return Boolean.valueOf(multiplePI); } else if (PROP_VIRTUAL_ROOT.equals(name)) { return virtualRoot; } else if (PROP_PRETTY_PRINT.equals(name)) { return Boolean.valueOf(prettyPrint); } else if (PROP_NAMESPACE_SEPARATOR.equals(name)) { return namespaceSeparator; } else if (PROP_NAMESPACE_DECLARATIONS.equals(name)) { return Boolean.valueOf(namespaceDeclarations); } else if (PROP_CUSTOM_REGEX.equals(name)) { return customRegex; } else if (PROP_CUSTOM_REPLACE_REGEX.equals(name)) { return customReplaceRegex; } else if (PROP_CUSTOM_REPLACE_SEQUENCE.equals(name)) { return customReplaceSequence; } else { throw new IllegalArgumentException("Unsupported property: " + name); } } } @Override public void setProperty(String name, Object value) throws IllegalArgumentException { if (super.isPropertySupported(name)) { super.setProperty(name, value); } else { // proprietary properties if (PROP_AUTO_ARRAY.equals(name)) { autoArray = ((Boolean) value).booleanValue(); } else if (PROP_AUTO_PRIMITIVE.equals(name)) { autoPrimitive = ((Boolean) value).booleanValue(); } else if (PROP_MULTIPLE_PI.equals(name)) { multiplePI = ((Boolean) value).booleanValue(); } else if (PROP_VIRTUAL_ROOT.equals(name)) { virtualRoot = value instanceof String ? QName.valueOf((String) value) : (QName) value; } else if (PROP_PRETTY_PRINT.equals(name)) { prettyPrint = ((Boolean) value).booleanValue(); } else if (PROP_NAMESPACE_SEPARATOR.equals(name)) { namespaceSeparator = (Character) value; } else if (PROP_NAMESPACE_DECLARATIONS.equals(name)) { namespaceDeclarations = ((Boolean) value).booleanValue(); } else if (PROP_CUSTOM_REGEX.equals(name) && value instanceof String) { customRegex = (String) value; } else if (PROP_CUSTOM_REPLACE_REGEX.equals(name) && value instanceof String) { customReplaceRegex = (String) value; } else if (PROP_CUSTOM_REPLACE_SEQUENCE.equals(name) && value instanceof String) { customReplaceSequence = (String) value; } else { throw new IllegalArgumentException("Unsupported property: " + name); } } } }