All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.json.xml.XMLSerializer Maven / Gradle / Ivy

Go to download

Java library for transforming beans, maps, collections, java arrays and XML to JSON.

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 );
 * <o class="object">
 <name type="string">json</name>
 <bool type="boolean">true</bool>
 <int type="number">1</int>
 </o>
 * 
 * JSONArray json = JSONArray.fromObject("[1,2,3]");
 * String xml = new XMLSerializer().write( json );
 * <a class="array">
 <e type="number">1</e>
 <e type="number">2</e>
 <e type="number">3</e>
 </a>
 * 
* * @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( "" ); }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 ); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy