net.sf.json.xml.XMLSerializer Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 net.sf.json.xml;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONFunction;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.Node;
import nu.xom.Serializer;
import nu.xom.Text;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility class for transforming JSON to XML an back.
* When transforming JSONObject and JSONArray instances to XML, this class will
* add hints for converting back to JSON.
* Examples:
*
*
* JSONObject json = JSONObject.fromObject("{\"name\":\"json\",\"bool\":true,\"int\":1}");
* String xml = new XMLSerializer().write( json );
*
json
true
1
*
*
*
* JSONArray json = JSONArray.fromObject("[1,2,3]");
* String xml = new XMLSerializer().write( json );
*
1
2
3
*
*
* @author Andres Almiray
*/
public class XMLSerializer {
private static final String[] EMPTY_ARRAY = new String[0];
private static final String JSON_PREFIX = "json_";
private static final Log log = LogFactory.getLog(XMLSerializer.class);
/** the name for an JSONArray Element */
private String arrayName;
/** the name for an JSONArray's element Element */
private String elementName;
/** list of properties to be expanded from child to parent */
private String[] expandableProperties;
private boolean forceTopLevelObject;
/** flag to be tolerant for incomplete namespace prefixes */
private boolean namespaceLenient;
/** Map of namespaces per element */
private Map namespacesPerElement = new TreeMap();
/** the name for an JSONObject Element */
private String objectName;
/** flag for trimming namespace prefix from element name */
private boolean removeNamespacePrefixFromElements;
/** the name for the root Element */
private String rootName;
/** Map of namespaces for root element */
private Map rootNamespace = new TreeMap();
/** flag for skipping namespaces while reading */
private boolean skipNamespaces;
/** flag for skipping whitespace elements while reading */
private boolean skipWhitespace;
/** flag for trimming spaces from string values */
private boolean trimSpaces;
/** flag for type hints naming compatibility */
private boolean typeHintsCompatibility;
/** flag for adding JSON types hints as attributes */
private boolean typeHintsEnabled;
/**
* Creates a new XMLSerializer with default options.
*
* objectName
: 'o'
* arrayName
: 'a'
* elementName
: 'e'
* typeHinstEnabled
: true
* typeHinstCompatibility
: true
* namespaceLenient
: false
* expandableProperties
: []
* skipNamespaces
: false
* removeNameSpacePrefixFromElement
: false
* trimSpaces
: false
*
*/
public XMLSerializer() {
setObjectName("o");
setArrayName("a");
setElementName("e");
setTypeHintsEnabled(true);
setTypeHintsCompatibility(true);
setNamespaceLenient(false);
setSkipNamespaces(false);
setRemoveNamespacePrefixFromElements(false);
setTrimSpaces(false);
setExpandableProperties(EMPTY_ARRAY);
setSkipNamespaces(false);
}
/**
* Adds a namespace declaration to the root element.
*
* @param prefix
* namespace prefix
* @param uri
* namespace uri
*/
public void addNamespace(String prefix, String uri) {
addNamespace(prefix, uri, null);
}
/**
* Adds a namespace declaration to an element.
* If the elementName param is null or blank, the namespace declaration will be
* added to the root element.
*
* @param prefix
* namespace prefix
* @param uri
* namespace uri
* @param elementName
* name of target element
*/
public void addNamespace(String prefix, String uri, String elementName) {
if (StringUtils.isBlank(uri)) {
return;
}
if (prefix == null) {
prefix = "";
}
if (StringUtils.isBlank(elementName)) {
rootNamespace.put(prefix.trim(), uri.trim());
} else {
Map nameSpaces = (Map) namespacesPerElement.get(elementName);
if (nameSpaces == null) {
nameSpaces = new TreeMap();
namespacesPerElement.put(elementName, nameSpaces);
}
nameSpaces.put(prefix, uri);
}
}
/**
* Removes all namespaces declarations (from root an elements).
*/
public void clearNamespaces() {
rootNamespace.clear();
namespacesPerElement.clear();
}
/**
* Removes all namespace declarations from an element.
* If the elementName param is null or blank, the declarations will be removed
* from the root element.
*
* @param elementName
* name of target element
*/
public void clearNamespaces(String elementName) {
if (StringUtils.isBlank(elementName)) {
rootNamespace.clear();
} else {
namespacesPerElement.remove(elementName);
}
}
/**
* Returns the name used for JSONArray.
*/
public String getArrayName() {
return arrayName;
}
/**
* Returns the name used for JSONArray elements.
*/
public String getElementName() {
return elementName;
}
/**
* Returns a list of properties to be expanded from child to parent.
*/
public String[] getExpandableProperties() {
return expandableProperties;
}
/**
* Returns the name used for JSONArray.
*/
public String getObjectName() {
return objectName;
}
/**
* Returns the name used for the root element.
*/
public String getRootName() {
return rootName;
}
public boolean isForceTopLevelObject() {
return forceTopLevelObject;
}
/**
* Returns wether this serializer is tolerant to namespaces without URIs or not.
*/
public boolean isNamespaceLenient() {
return namespaceLenient;
}
/**
* Returns wether this serializer will remove namespace prefix from elements or
* not.
*/
public boolean isRemoveNamespacePrefixFromElements() {
return removeNamespacePrefixFromElements;
}
/**
* Returns wether this serializer will skip adding namespace declarations to
* elements or not.
*/
public boolean isSkipNamespaces() {
return skipNamespaces;
}
/**
* Returns wether this serializer will skip whitespace or not.
*/
public boolean isSkipWhitespace() {
return skipWhitespace;
}
/**
* Returns wether this serializer will trim leading and trealing whitespace from
* values or not.
*/
public boolean isTrimSpaces() {
return trimSpaces;
}
/**
* Returns true if types hints will have a 'json_' prefix or not.
*/
public boolean isTypeHintsCompatibility() {
return typeHintsCompatibility;
}
/**
* Returns true if JSON types will be included as attributes.
*/
public boolean isTypeHintsEnabled() {
return typeHintsEnabled;
}
/**
* Creates a JSON value from a XML string.
*
* @param xml
* A well-formed xml document in a String
* @return a JSONNull, JSONObject or JSONArray
* @throws JSONException
* if the conversion from XML to JSON can't be made for I/O or
* format reasons.
*/
public JSON read(String xml) {
JSON json = null;
try {
Document doc = new Builder().build(new StringReader(xml));
Element root = doc.getRootElement();
if (isNullObject(root)) {
return JSONNull.getInstance();
}
String defaultType = getType(root, JSONTypes.STRING);
if (isArray(root, true)) {
json = processArrayElement(root, defaultType);
if (forceTopLevelObject) {
String key = removeNamespacePrefix(root.getQualifiedName());
json = new JSONObject().element(key, json);
}
} else {
json = processObjectElement(root, defaultType);
if (forceTopLevelObject) {
String key = removeNamespacePrefix(root.getQualifiedName());
json = new JSONObject().element(key, json);
}
}
} catch (JSONException jsone) {
throw jsone;
} catch (Exception e) {
throw new JSONException(e);
}
return json;
}
/**
* Creates a JSON value from a File.
*
* @param file
* @return a JSONNull, JSONObject or JSONArray
* @throws JSONException
* if the conversion from XML to JSON can't be made for I/O or
* format reasons.
*/
public JSON readFromFile(File file) {
if (file == null) {
throw new JSONException("File is null");
}
if (!file.canRead()) {
throw new JSONException("Can't read input file");
}
if (file.isDirectory()) {
throw new JSONException("File is a directory");
}
try {
return readFromStream(new FileInputStream(file));
} catch (IOException ioe) {
throw new JSONException(ioe);
}
}
/**
* Creates a JSON value from a File.
*
* @param path
* @return a JSONNull, JSONObject or JSONArray
* @throws JSONException
* if the conversion from XML to JSON can't be made for I/O or
* format reasons.
*/
public JSON readFromFile(String path) {
return readFromStream(Thread.currentThread().getContextClassLoader().getResourceAsStream(path));
}
/**
* Creates a JSON value from an input stream.
*
* @param stream
* @return a JSONNull, JSONObject or JSONArray
* @throws JSONException
* if the conversion from XML to JSON can't be made for I/O or
* format reasons.
*/
public JSON readFromStream(InputStream stream) {
try {
StringBuffer xml = new StringBuffer();
BufferedReader in = new BufferedReader(new InputStreamReader(stream));
String line = null;
while ((line = in.readLine()) != null) {
xml.append(line);
}
return read(xml.toString());
} catch (IOException ioe) {
throw new JSONException(ioe);
}
}
/**
* Removes a namespace from the root element.
*
* @param prefix
* namespace prefix
*/
public void removeNamespace(String prefix) {
removeNamespace(prefix, null);
}
/**
* Removes a namespace from the root element.
* If the elementName is null or blank, the namespace will be removed from the
* root element.
*
* @param prefix
* namespace prefix
* @param elementName
* name of target element
*/
public void removeNamespace(String prefix, String elementName) {
if (prefix == null) {
prefix = "";
}
if (StringUtils.isBlank(elementName)) {
rootNamespace.remove(prefix.trim());
} else {
Map nameSpaces = (Map) namespacesPerElement.get(elementName);
nameSpaces.remove(prefix);
}
}
/**
* Sets the name used for JSONArray.
* Default is 'a'.
*/
public void setArrayName(String arrayName) {
this.arrayName = StringUtils.isBlank(arrayName) ? "a" : arrayName;
}
/**
* Sets the name used for JSONArray elements.
* Default is 'e'.
*/
public void setElementName(String elementName) {
this.elementName = StringUtils.isBlank(elementName) ? "e" : elementName;
}
/**
* Sets the list of properties to be expanded from child to parent.
*/
public void setExpandableProperties(String[] expandableProperties) {
this.expandableProperties = expandableProperties == null ? EMPTY_ARRAY : expandableProperties;
}
public void setForceTopLevelObject(boolean forceTopLevelObject) {
this.forceTopLevelObject = forceTopLevelObject;
}
/**
* Sets the namespace declaration to the root element.
* Any previous values are discarded.
*
* @param prefix
* namespace prefix
* @param uri
* namespace uri
*/
public void setNamespace(String prefix, String uri) {
setNamespace(prefix, uri, null);
}
/**
* Adds a namespace declaration to an element.
* Any previous values are discarded. If the elementName param is null or blank,
* the namespace declaration will be added to the root element.
*
* @param prefix
* namespace prefix
* @param uri
* namespace uri
* @param elementName
* name of target element
*/
public void setNamespace(String prefix, String uri, String elementName) {
if (StringUtils.isBlank(uri)) {
return;
}
if (prefix == null) {
prefix = "";
}
if (StringUtils.isBlank(elementName)) {
rootNamespace.clear();
rootNamespace.put(prefix.trim(), uri.trim());
} else {
Map nameSpaces = (Map) namespacesPerElement.get(elementName);
if (nameSpaces == null) {
nameSpaces = new TreeMap();
namespacesPerElement.put(elementName, nameSpaces);
}
nameSpaces.clear();
nameSpaces.put(prefix, uri);
}
}
/**
* Sets wether this serializer is tolerant to namespaces without URIs or not.
*/
public void setNamespaceLenient(boolean namespaceLenient) {
this.namespaceLenient = namespaceLenient;
}
/**
* Sets the name used for JSONObject.
* Default is 'o'.
*/
public void setObjectName(String objectName) {
this.objectName = StringUtils.isBlank(objectName) ? "o" : objectName;
}
/**
* Sets if this serializer will remove namespace prefix from elements when
* reading.
*/
public void setRemoveNamespacePrefixFromElements(boolean removeNamespacePrefixFromElements) {
this.removeNamespacePrefixFromElements = removeNamespacePrefixFromElements;
}
/**
* Sets the name used for the root element.
*/
public void setRootName(String rootName) {
this.rootName = StringUtils.isBlank(rootName) ? null : rootName;
}
/**
* Sets if this serializer will skip adding namespace declarations to elements
* when reading.
*/
public void setSkipNamespaces(boolean skipNamespaces) {
this.skipNamespaces = skipNamespaces;
}
/**
* Sets if this serializer will skip whitespace when reading.
*/
public void setSkipWhitespace(boolean skipWhitespace) {
this.skipWhitespace = skipWhitespace;
}
/**
* Sets if this serializer will trim leading and trealing whitespace from values
* when reading.
*/
public void setTrimSpaces(boolean trimSpaces) {
this.trimSpaces = trimSpaces;
}
/**
* Sets wether types hints will have a 'json_' prefix or not.
*/
public void setTypeHintsCompatibility(boolean typeHintsCompatibility) {
this.typeHintsCompatibility = typeHintsCompatibility;
}
/**
* Sets wether JSON types will be included as attributes.
*/
public void setTypeHintsEnabled(boolean typeHintsEnabled) {
this.typeHintsEnabled = typeHintsEnabled;
}
/**
* Writes a JSON value into a XML string with UTF-8 encoding.
*
* @param json
* The JSON value to transform
* @return a String representation of a well-formed xml document.
* @throws JSONException
* if the conversion from JSON to XML can't be made for I/O reasons.
*/
public String write(JSON json) {
return write(json, null);
}
/**
* Writes a JSON value into a XML string with an specific encoding.
* If the encoding string is null it will use UTF-8.
*
* @param json
* The JSON value to transform
* @param encoding
* The xml encoding to use
* @return a String representation of a well-formed xml document.
* @throws JSONException
* if the conversion from JSON to XML can't be made for I/O reasons
* or the encoding is not supported.
*/
public String write(JSON json, String encoding) {
if (JSONNull.getInstance().equals(json)) {
Element root = null;
root = newElement(getRootName() == null ? getObjectName() : getRootName());
root.addAttribute(new Attribute(addJsonPrefix("null"), "true"));
Document doc = new Document(root);
return writeDocument(doc, encoding);
} else if (json instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) json;
Element root = processJSONArray(jsonArray,
newElement(getRootName() == null ? getArrayName() : getRootName()), expandableProperties);
Document doc = new Document(root);
return writeDocument(doc, encoding);
} else {
JSONObject jsonObject = (JSONObject) json;
Element root = null;
if (jsonObject.isNullObject()) {
root = newElement(getObjectName());
root.addAttribute(new Attribute(addJsonPrefix("null"), "true"));
} else {
root = processJSONObject(jsonObject,
newElement(getRootName() == null ? getObjectName() : getRootName()), expandableProperties,
true);
}
Document doc = new Document(root);
return writeDocument(doc, encoding);
}
}
private String addJsonPrefix(String str) {
if (!isTypeHintsCompatibility()) {
return JSON_PREFIX + str;
}
return str;
}
private void addNameSpaceToElement(Element element) {
String elementName = null;
if (element instanceof CustomElement) {
elementName = ((CustomElement) element).getQName();
} else {
elementName = element.getQualifiedName();
}
Map nameSpaces = (Map) namespacesPerElement.get(elementName);
if (nameSpaces != null && !nameSpaces.isEmpty()) {
setNamespaceLenient(true);
for (Iterator entries = nameSpaces.entrySet().iterator(); entries.hasNext();) {
Map.Entry entry = (Map.Entry) entries.next();
String prefix = (String) entry.getKey();
String uri = (String) entry.getValue();
if (StringUtils.isBlank(prefix)) {
element.setNamespaceURI(uri);
} else {
element.addNamespaceDeclaration(prefix, uri);
}
}
}
}
private boolean checkChildElements(Element element, boolean isTopLevel) {
int childCount = element.getChildCount();
Elements elements = element.getChildElements();
int elementCount = elements.size();
if (childCount == 1 && element.getChild(0) instanceof Text) {
return isTopLevel;
}
if (childCount == elementCount) {
if (elementCount == 0) {
return true;
}
if (elementCount == 1) {
if (skipWhitespace || element.getChild(0) instanceof Text) {
return true;
} else {
return false;
}
}
}
if (childCount > elementCount) {
for (int i = 0; i < childCount; i++) {
Node node = element.getChild(i);
if (node instanceof Text) {
Text text = (Text) node;
if (StringUtils.isNotBlank(StringUtils.strip(text.getValue())) && !skipWhitespace) {
return false;
}
}
}
}
String childName = elements.get(0).getQualifiedName();
for (int i = 1; i < elementCount; i++) {
if (childName.compareTo(elements.get(i).getQualifiedName()) != 0) {
return false;
}
}
return true;
}
private String getClass(Element element) {
Attribute attribute = element.getAttribute(addJsonPrefix("class"));
String clazz = null;
if (attribute != null) {
String clazzText = attribute.getValue().trim();
if (JSONTypes.OBJECT.compareToIgnoreCase(clazzText) == 0) {
clazz = JSONTypes.OBJECT;
} else if (JSONTypes.ARRAY.compareToIgnoreCase(clazzText) == 0) {
clazz = JSONTypes.ARRAY;
}
}
return clazz;
}
private String getType(Element element) {
return getType(element, null);
}
private String getType(Element element, String defaultType) {
Attribute attribute = element.getAttribute(addJsonPrefix("type"));
String type = null;
if (attribute != null) {
String typeText = attribute.getValue().trim();
if (JSONTypes.BOOLEAN.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.BOOLEAN;
} else if (JSONTypes.NUMBER.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.NUMBER;
} else if (JSONTypes.INTEGER.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.INTEGER;
} else if (JSONTypes.FLOAT.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.FLOAT;
} else if (JSONTypes.OBJECT.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.OBJECT;
} else if (JSONTypes.ARRAY.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.ARRAY;
} else if (JSONTypes.STRING.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.STRING;
} else if (JSONTypes.FUNCTION.compareToIgnoreCase(typeText) == 0) {
type = JSONTypes.FUNCTION;
}
} else {
if (defaultType != null) {
log.info("Using default type " + defaultType);
type = defaultType;
}
}
return type;
}
private boolean hasNamespaces(Element element) {
int namespaces = 0;
for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) {
String prefix = element.getNamespacePrefix(i);
String uri = element.getNamespaceURI(prefix);
if (StringUtils.isBlank(uri)) {
continue;
}
namespaces++;
}
return namespaces > 0;
}
private boolean isArray(Element element, boolean isTopLevel) {
boolean isArray = false;
String clazz = getClass(element);
if (clazz != null && clazz.equals(JSONTypes.ARRAY)) {
isArray = true;
} else if (element.getAttributeCount() == 0) {
isArray = checkChildElements(element, isTopLevel);
} else if (element.getAttributeCount() == 1 && (element.getAttribute(addJsonPrefix("class")) != null
|| element.getAttribute(addJsonPrefix("type")) != null)) {
isArray = checkChildElements(element, isTopLevel);
} else if (element.getAttributeCount() == 2 && (element.getAttribute(addJsonPrefix("class")) != null
&& element.getAttribute(addJsonPrefix("type")) != null)) {
isArray = checkChildElements(element, isTopLevel);
}
if (isArray) {
// check namespace
for (int j = 0; j < element.getNamespaceDeclarationCount(); j++) {
String prefix = element.getNamespacePrefix(j);
String uri = element.getNamespaceURI(prefix);
if (!StringUtils.isBlank(uri)) {
return false;
}
}
}
return isArray;
}
private boolean isFunction(Element element) {
int attrCount = element.getAttributeCount();
if (attrCount > 0) {
Attribute typeAttr = element.getAttribute(addJsonPrefix("type"));
Attribute paramsAttr = element.getAttribute(addJsonPrefix("params"));
if (attrCount == 1 && paramsAttr != null) {
return true;
}
if (attrCount == 2 && paramsAttr != null && typeAttr != null
&& (typeAttr.getValue().compareToIgnoreCase(JSONTypes.STRING) == 0
|| typeAttr.getValue().compareToIgnoreCase(JSONTypes.FUNCTION) == 0)) {
return true;
}
}
return false;
}
private boolean isNullObject(Element element) {
if (element.getChildCount() == 0) {
if (element.getAttributeCount() == 0) {
return true;
} else if (element.getAttribute(addJsonPrefix("null")) != null) {
return true;
} else if (element.getAttributeCount() == 1 && (element.getAttribute(addJsonPrefix("class")) != null
|| element.getAttribute(addJsonPrefix("type")) != null)) {
return true;
} else if (element.getAttributeCount() == 2 && (element.getAttribute(addJsonPrefix("class")) != null
&& element.getAttribute(addJsonPrefix("type")) != null)) {
return true;
}
}
if (skipWhitespace && element.getChildCount() == 1 && element.getChild(0) instanceof Text) {
return true;
}
return false;
}
private boolean isObject(Element element, boolean isTopLevel) {
boolean isObject = false;
if (!isArray(element, isTopLevel) && !isFunction(element)) {
if (hasNamespaces(element)) {
return true;
}
int attributeCount = element.getAttributeCount();
if (attributeCount > 0) {
int attrs = element.getAttribute(addJsonPrefix("null")) == null ? 0 : 1;
attrs += element.getAttribute(addJsonPrefix("class")) == null ? 0 : 1;
attrs += element.getAttribute(addJsonPrefix("type")) == null ? 0 : 1;
switch (attributeCount) {
case 1:
if (attrs == 0) {
return true;
}
break;
case 2:
if (attrs < 2) {
return true;
}
break;
case 3:
if (attrs < 3) {
return true;
}
break;
default:
return true;
}
}
int childCount = element.getChildCount();
if (childCount == 1 && element.getChild(0) instanceof Text) {
return isTopLevel;
}
isObject = true;
}
return isObject;
}
private Element newElement(String name) {
if (name.indexOf(':') != -1) {
namespaceLenient = true;
}
return namespaceLenient ? new CustomElement(name) : new Element(name);
}
private JSON processArrayElement(Element element, String defaultType) {
JSONArray jsonArray = new JSONArray();
// process children (including text)
int childCount = element.getChildCount();
for (int i = 0; i < childCount; i++) {
Node child = element.getChild(i);
if (child instanceof Text) {
Text text = (Text) child;
if (StringUtils.isNotBlank(StringUtils.strip(text.getValue()))) {
jsonArray.element(text.getValue());
}
} else if (child instanceof Element) {
setValue(jsonArray, (Element) child, defaultType);
}
}
return jsonArray;
}
private Object processElement(Element element, String type) {
if (isNullObject(element)) {
return JSONNull.getInstance();
} else if (isArray(element, false)) {
return processArrayElement(element, type);
} else if (isObject(element, false)) {
return processObjectElement(element, type);
} else {
return trimSpaceFromValue(element.getValue());
}
}
private Element processJSONArray(JSONArray array, Element root, String[] expandableProperties) {
int l = array.size();
for (int i = 0; i < l; i++) {
Object value = array.get(i);
Element element = processJSONValue(value, root, null, expandableProperties);
root.appendChild(element);
}
return root;
}
private Element processJSONObject(JSONObject jsonObject, Element root, String[] expandableProperties,
boolean isRoot) {
if (jsonObject.isNullObject()) {
root.addAttribute(new Attribute(addJsonPrefix("null"), "true"));
return root;
} else if (jsonObject.isEmpty()) {
return root;
}
if (isRoot) {
if (!rootNamespace.isEmpty()) {
setNamespaceLenient(true);
for (Iterator entries = rootNamespace.entrySet().iterator(); entries.hasNext();) {
Map.Entry entry = (Map.Entry) entries.next();
String prefix = (String) entry.getKey();
String uri = (String) entry.getValue();
if (StringUtils.isBlank(prefix)) {
root.setNamespaceURI(uri);
} else {
root.addNamespaceDeclaration(prefix, uri);
}
}
}
}
addNameSpaceToElement(root);
Object[] names = jsonObject.names().toArray();
Arrays.sort(names);
Element element = null;
for (int i = 0; i < names.length; i++) {
String name = (String) names[i];
Object value = jsonObject.get(name);
if (name.startsWith("@xmlns")) {
setNamespaceLenient(true);
int colon = name.indexOf(':');
if (colon == -1) {
// do not override if already defined by nameSpaceMaps
if (StringUtils.isBlank(root.getNamespaceURI())) {
root.setNamespaceURI(String.valueOf(value));
}
} else {
String prefix = name.substring(colon + 1);
if (StringUtils.isBlank(root.getNamespaceURI(prefix))) {
root.addNamespaceDeclaration(prefix, String.valueOf(value));
}
}
} else if (name.startsWith("@")) {
root.addAttribute(new Attribute(name.substring(1), String.valueOf(value)));
} else if (name.equals("#text")) {
if (value instanceof JSONArray) {
root.appendChild(((JSONArray) value).join("", true));
} else {
root.appendChild(String.valueOf(value));
}
} else if (value instanceof JSONArray
&& (((JSONArray) value).isExpandElements() || ArrayUtils.contains(expandableProperties, name))) {
JSONArray array = (JSONArray) value;
int l = array.size();
for (int j = 0; j < l; j++) {
Object item = array.get(j);
element = newElement(name);
if (item instanceof JSONObject) {
element = processJSONValue((JSONObject) item, root, element, expandableProperties);
} else if (item instanceof JSONArray) {
element = processJSONValue((JSONArray) item, root, element, expandableProperties);
} else {
element = processJSONValue(item, root, element, expandableProperties);
}
addNameSpaceToElement(element);
root.appendChild(element);
}
} else {
element = newElement(name);
element = processJSONValue(value, root, element, expandableProperties);
addNameSpaceToElement(element);
root.appendChild(element);
}
}
return root;
}
private Element processJSONValue(Object value, Element root, Element target, String[] expandableProperties) {
if (target == null) {
target = newElement(getElementName());
}
if (JSONUtils.isBoolean(value)) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("type"), JSONTypes.BOOLEAN));
}
target.appendChild(value.toString());
} else if (JSONUtils.isNumber(value)) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("type"), JSONTypes.NUMBER));
}
target.appendChild(value.toString());
} else if (JSONUtils.isFunction(value)) {
if (value instanceof String) {
value = JSONFunction.parse((String) value);
}
JSONFunction func = (JSONFunction) value;
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("type"), JSONTypes.FUNCTION));
}
String params = ArrayUtils.toString(func.getParams());
params = params.substring(1);
params = params.substring(0, params.length() - 1);
target.addAttribute(new Attribute(addJsonPrefix("params"), params));
target.appendChild(new Text(""));
} else if (JSONUtils.isString(value)) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("type"), JSONTypes.STRING));
}
target.appendChild(value.toString());
} else if (value instanceof JSONArray) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("class"), JSONTypes.ARRAY));
}
target = processJSONArray((JSONArray) value, target, expandableProperties);
} else if (value instanceof JSONObject) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("class"), JSONTypes.OBJECT));
}
target = processJSONObject((JSONObject) value, target, expandableProperties, false);
} else if (JSONUtils.isNull(value)) {
if (isTypeHintsEnabled()) {
target.addAttribute(new Attribute(addJsonPrefix("class"), JSONTypes.OBJECT));
}
target.addAttribute(new Attribute(addJsonPrefix("null"), "true"));
}
return target;
}
private JSON processObjectElement(Element element, String defaultType) {
if (isNullObject(element)) {
return JSONNull.getInstance();
}
JSONObject jsonObject = new JSONObject();
if (!skipNamespaces) {
for (int j = 0; j < element.getNamespaceDeclarationCount(); j++) {
String prefix = element.getNamespacePrefix(j);
String uri = element.getNamespaceURI(prefix);
if (StringUtils.isBlank(uri)) {
continue;
}
if (!StringUtils.isBlank(prefix)) {
prefix = ":" + prefix;
}
setOrAccumulate(jsonObject, "@xmlns" + prefix, trimSpaceFromValue(uri));
}
}
// process attributes first
int attrCount = element.getAttributeCount();
for (int i = 0; i < attrCount; i++) {
Attribute attr = element.getAttribute(i);
String attrname = attr.getQualifiedName();
if (isTypeHintsEnabled() && (addJsonPrefix("class").compareToIgnoreCase(attrname) == 0
|| addJsonPrefix("type").compareToIgnoreCase(attrname) == 0)) {
continue;
}
String attrvalue = attr.getValue();
setOrAccumulate(jsonObject, "@" + removeNamespacePrefix(attrname), trimSpaceFromValue(attrvalue));
}
// process children (including text)
int childCount = element.getChildCount();
for (int i = 0; i < childCount; i++) {
Node child = element.getChild(i);
if (child instanceof Text) {
Text text = (Text) child;
if (StringUtils.isNotBlank(StringUtils.strip(text.getValue()))) {
setOrAccumulate(jsonObject, "#text", trimSpaceFromValue(text.getValue()));
}
} else if (child instanceof Element) {
setValue(jsonObject, (Element) child, defaultType);
}
}
return jsonObject;
}
private String removeNamespacePrefix(String name) {
if (isRemoveNamespacePrefixFromElements()) {
int colon = name.indexOf(':');
return colon != -1 ? name.substring(colon + 1) : name;
}
return name;
}
private void setOrAccumulate(JSONObject jsonObject, String key, Object value) {
if (jsonObject.has(key)) {
jsonObject.accumulate(key, value);
Object val = jsonObject.get(key);
if (val instanceof JSONArray) {
((JSONArray) val).setExpandElements(true);
}
} else {
jsonObject.element(key, value);
}
}
private void setValue(JSONArray jsonArray, Element element, String defaultType) {
String clazz = getClass(element);
String type = getType(element);
type = (type == null) ? defaultType : type;
if (hasNamespaces(element) && !skipNamespaces) {
jsonArray.element(simplifyValue(null, processElement(element, type)));
return;
} else if (element.getAttributeCount() > 0) {
if (isFunction(element)) {
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
String[] params = null;
String text = element.getValue();
params = StringUtils.split(paramsAttribute.getValue(), ",");
jsonArray.element(new JSONFunction(params, text));
return;
} else {
jsonArray.element(simplifyValue(null, processElement(element, type)));
return;
}
}
boolean classProcessed = false;
if (clazz != null) {
if (clazz.compareToIgnoreCase(JSONTypes.ARRAY) == 0) {
jsonArray.element(processArrayElement(element, type));
classProcessed = true;
} else if (clazz.compareToIgnoreCase(JSONTypes.OBJECT) == 0) {
jsonArray.element(simplifyValue(null, processObjectElement(element, type)));
classProcessed = true;
}
}
if (!classProcessed) {
if (type.compareToIgnoreCase(JSONTypes.BOOLEAN) == 0) {
jsonArray.element(Boolean.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.NUMBER) == 0) {
// try integer first
try {
jsonArray.element(Integer.valueOf(element.getValue()));
} catch (NumberFormatException e) {
jsonArray.element(Double.valueOf(element.getValue()));
}
} else if (type.compareToIgnoreCase(JSONTypes.INTEGER) == 0) {
jsonArray.element(Integer.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.FLOAT) == 0) {
jsonArray.element(Double.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.FUNCTION) == 0) {
String[] params = null;
String text = element.getValue();
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
if (paramsAttribute != null) {
params = StringUtils.split(paramsAttribute.getValue(), ",");
}
jsonArray.element(new JSONFunction(params, text));
} else if (type.compareToIgnoreCase(JSONTypes.STRING) == 0) {
// see if by any chance has a 'params' attribute
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
if (paramsAttribute != null) {
String[] params = null;
String text = element.getValue();
params = StringUtils.split(paramsAttribute.getValue(), ",");
jsonArray.element(new JSONFunction(params, text));
} else {
if (isArray(element, false)) {
jsonArray.element(processArrayElement(element, defaultType));
} else if (isObject(element, false)) {
jsonArray.element(simplifyValue(null, processObjectElement(element, defaultType)));
} else {
jsonArray.element(trimSpaceFromValue(element.getValue()));
}
}
}
}
}
private void setValue(JSONObject jsonObject, Element element, String defaultType) {
String clazz = getClass(element);
String type = getType(element);
type = (type == null) ? defaultType : type;
String key = removeNamespacePrefix(element.getQualifiedName());
if (hasNamespaces(element) && !skipNamespaces) {
setOrAccumulate(jsonObject, key, simplifyValue(jsonObject, processElement(element, type)));
return;
} else if (element.getAttributeCount() > 0) {
if (isFunction(element)) {
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
String text = element.getValue();
String[] params = StringUtils.split(paramsAttribute.getValue(), ",");
setOrAccumulate(jsonObject, key, new JSONFunction(params, text));
return;
} /*
* else{ setOrAccumulate( jsonObject, key, simplifyValue( jsonObject,
* processElement( element, type ) ) ); return; }
*/
}
boolean classProcessed = false;
if (clazz != null) {
if (clazz.compareToIgnoreCase(JSONTypes.ARRAY) == 0) {
setOrAccumulate(jsonObject, key, processArrayElement(element, type));
classProcessed = true;
} else if (clazz.compareToIgnoreCase(JSONTypes.OBJECT) == 0) {
setOrAccumulate(jsonObject, key, simplifyValue(jsonObject, processObjectElement(element, type)));
classProcessed = true;
}
}
if (!classProcessed) {
if (type.compareToIgnoreCase(JSONTypes.BOOLEAN) == 0) {
setOrAccumulate(jsonObject, key, Boolean.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.NUMBER) == 0) {
// try integer first
try {
setOrAccumulate(jsonObject, key, Integer.valueOf(element.getValue()));
} catch (NumberFormatException e) {
setOrAccumulate(jsonObject, key, Double.valueOf(element.getValue()));
}
} else if (type.compareToIgnoreCase(JSONTypes.INTEGER) == 0) {
setOrAccumulate(jsonObject, key, Integer.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.FLOAT) == 0) {
setOrAccumulate(jsonObject, key, Double.valueOf(element.getValue()));
} else if (type.compareToIgnoreCase(JSONTypes.FUNCTION) == 0) {
String[] params = null;
String text = element.getValue();
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
if (paramsAttribute != null) {
params = StringUtils.split(paramsAttribute.getValue(), ",");
}
setOrAccumulate(jsonObject, key, new JSONFunction(params, text));
} else if (type.compareToIgnoreCase(JSONTypes.STRING) == 0) {
// see if by any chance has a 'params' attribute
Attribute paramsAttribute = element.getAttribute(addJsonPrefix("params"));
if (paramsAttribute != null) {
String[] params = null;
String text = element.getValue();
params = StringUtils.split(paramsAttribute.getValue(), ",");
setOrAccumulate(jsonObject, key, new JSONFunction(params, text));
} else {
if (isArray(element, false)) {
setOrAccumulate(jsonObject, key, processArrayElement(element, defaultType));
} else if (isObject(element, false)) {
setOrAccumulate(jsonObject, key,
simplifyValue(jsonObject, processObjectElement(element, defaultType)));
} else {
setOrAccumulate(jsonObject, key, trimSpaceFromValue(element.getValue()));
}
}
}
}
}
private Object simplifyValue(JSONObject parent, Object json) {
if (json instanceof JSONObject) {
JSONObject object = (JSONObject) json;
if (parent != null) {
// remove all duplicated @xmlns from child
for (Iterator entries = parent.entrySet().iterator(); entries.hasNext();) {
Map.Entry entry = (Map.Entry) entries.next();
String key = (String) entry.getKey();
Object value = entry.getValue();
if (key.startsWith("@xmlns") && value.equals(object.opt(key))) {
object.remove(key);
}
}
}
if (object.size() == 1 && object.has("#text")) {
return object.get("#text");
}
}
return json;
}
private String trimSpaceFromValue(String value) {
if (isTrimSpaces()) {
return value.trim();
}
return value;
}
private String writeDocument(Document doc, String encoding) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
XomSerializer serializer = (encoding == null) ? new XomSerializer(baos) : new XomSerializer(baos, encoding);
serializer.write(doc);
encoding = serializer.getEncoding();
} catch (IOException ioe) {
throw new JSONException(ioe);
}
String str = null;
try {
str = baos.toString(encoding);
} catch (UnsupportedEncodingException uee) {
throw new JSONException(uee);
}
return str;
}
private static class CustomElement extends Element {
private static String getName(String name) {
int colon = name.indexOf(':');
if (colon != -1) {
return name.substring(colon + 1);
}
return name;
}
private static String getPrefix(String name) {
int colon = name.indexOf(':');
if (colon != -1) {
return name.substring(0, colon);
}
return "";
}
private String prefix;
public CustomElement(String name) {
super(CustomElement.getName(name));
prefix = CustomElement.getPrefix(name);
}
public final String getQName() {
if (prefix.length() == 0) {
return getLocalName();
} else {
return prefix + ":" + getLocalName();
}
}
}
private class XomSerializer extends Serializer {
public XomSerializer(OutputStream out) {
super(out);
}
public XomSerializer(OutputStream out, String encoding) throws UnsupportedEncodingException {
super(out, encoding);
}
protected void write(Text text) throws IOException {
String value = text.getValue();
if (value.startsWith("")) {
value = value.substring(9);
value = value.substring(0, value.length() - 3);
writeRaw("");
} else {
super.write(text);
}
}
protected void writeEmptyElementTag(Element element) throws IOException {
if (element instanceof CustomElement && isNamespaceLenient()) {
writeTagBeginning((CustomElement) element);
writeRaw("/>");
} else {
super.writeEmptyElementTag(element);
}
}
protected void writeEndTag(Element element) throws IOException {
if (element instanceof CustomElement && isNamespaceLenient()) {
writeRaw("");
writeRaw(((CustomElement) element).getQName());
writeRaw(">");
} else {
super.writeEndTag(element);
}
}
protected void writeNamespaceDeclaration(String prefix, String uri) throws IOException {
if (!StringUtils.isBlank(uri)) {
super.writeNamespaceDeclaration(prefix, uri);
}
}
protected void writeStartTag(Element element) throws IOException {
if (element instanceof CustomElement && isNamespaceLenient()) {
writeTagBeginning((CustomElement) element);
writeRaw(">");
} else {
super.writeStartTag(element);
}
}
private void writeTagBeginning(CustomElement element) throws IOException {
writeRaw("<");
writeRaw(element.getQName());
writeAttributes(element);
writeNamespaceDeclarations(element);
}
}
}