/* ############################################################################### # # # Copyright (C) 2011-2016 OpenMEAP, Inc. # # Credits to Jonathan Schang & Rob Thacher # # # # Released under the LGPLv3 # # # # OpenMEAP is free software: you can redistribute it and/or modify # # it under the terms of the GNU Lesser General Public License as published # # by the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # OpenMEAP is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU Lesser General Public License for more details. # # # # You should have received a copy of the GNU Lesser General Public License # # along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. # # # ############################################################################### */ package com.openmeap.json; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import com.openmeap.json.HasJSONProperties; import com.openmeap.json.Enum; import com.openmeap.thirdparty.org.json.me.JSONArray; import com.openmeap.thirdparty.org.json.me.JSONException; import com.openmeap.thirdparty.org.json.me.JSONObject; /** * Converts an object hierarchy into a JSON representation. * Only looks at the JSONProperty annotated getter-methods. * Limited Hashtable support, but statically-typed arrays are good. * Assumes camel-case is desired. * Assumes the objects are instantiable without constructor arguments. * * @author schang */ public class JSONObjectBuilder { public Object fromJSON(JSONObject jsonObj, Object rootObject) throws JSONException { if( jsonObj==null ) { return null; } if( ! HasJSONProperties.class.isAssignableFrom(rootObject.getClass()) ) { throw new RuntimeException("The rootObject being converted to JSON must implement the HashJSONProperties interface."); } JSONProperty[] properties = ((HasJSONProperties)rootObject).getJSONProperties(); for( int jsonPropertyIdx=0; jsonPropertyIdx<properties.length; jsonPropertyIdx++ ) { JSONProperty property = properties[jsonPropertyIdx]; Class returnType = property.getReturnType(); String propertyName = property.getPropertyName(); try { // get the unparsed value from the JSONObject Object value = null; try { value = jsonObj.get(propertyName); } catch( JSONException e ) { continue; } if( value == JSONObject.NULL ) { continue; } if( Enum.class.isAssignableFrom(returnType) ) { property.getGetterSetter().setValue(rootObject,value); } else if( value instanceof JSONArray ) { JSONArray array = (JSONArray)value; Vector list = new Vector(); for( int i=0; i<array.length(); i++ ) { Object obj = array.get(i); if( obj instanceof JSONObject ) { Object newObj = (Object)returnType.newInstance(); list.addElement(fromJSON((JSONObject)obj,newObj)); } else { list.addElement(obj); } } if(property.getContainedType()!=null) { property.getGetterSetter().setValue(rootObject,list); } else { property.getGetterSetter().setValue(rootObject,toTypedArray(list)); } } else if( value instanceof JSONObject ) { Object obj = (Object)returnType.newInstance(); if( Hashtable.class.isAssignableFrom(returnType) ) { Hashtable table = (Hashtable)obj; JSONObject jsonMap = (JSONObject)value; Enumeration keysEnum = jsonMap.keys(); while(keysEnum.hasMoreElements()){ String key = (String)keysEnum.nextElement(); Object thisValue = jsonMap.get(key); if( thisValue instanceof JSONObject ) { Object newObj = (Object)returnType.newInstance(); table.put(key,fromJSON((JSONObject)thisValue,newObj)); } else { table.put(key,thisValue); } } property.getGetterSetter().setValue(rootObject, table); } else { // of the type correct for the property.getGetterSetter().setValue(rootObject, fromJSON((JSONObject)value,obj)); } } else if( isSimpleType(returnType) ) { property.getGetterSetter().setValue(rootObject, correctCasting(returnType,value)); } } catch( Exception e ) { throw new JSONException(e); } } return rootObject; } public JSONObject toJSON(Object obj) throws JSONException { if( obj==null ) { return null; } if( ! HasJSONProperties.class.isAssignableFrom(obj.getClass()) ) { throw new RuntimeException("The rootObject being converted to JSON must implement the HasJSONProperties interface."); } JSONProperty[] properties = ((HasJSONProperties)obj).getJSONProperties(); JSONObject jsonObj = new JSONObject(); // iterate over each JSONProperty annotated method for( int jsonPropertyIdx=0; jsonPropertyIdx<properties.length; jsonPropertyIdx++ ) { JSONProperty property = properties[jsonPropertyIdx]; // determine the method return type Class returnType = property.getReturnType(); Object value = property.getGetterSetter().getValue(obj); if(value==null) { continue; } if( returnType==null ) { throw new JSONException( obj.getClass().getName()+"."+property.getPropertyName() +" is annotated with JSONProperty, but has no return type." +" I can't work with this."); } // strip "get" off the front String propertyName = property.getPropertyName(); try { if( Enum.class.isAssignableFrom(returnType) ) { Enum ret = (Enum)value; jsonObj.put(propertyName, ret.value()); } else if( isSimpleType(returnType) ) { jsonObj.put(propertyName, handleSimpleType(returnType,property.getGetterSetter().getValue(obj)) ); } else { if( returnType.isArray() ) { Object[] returnValues = (Object[])value; JSONArray jsonArray = new JSONArray(); for( int returnValueIdx=0; returnValueIdx<returnValues.length; returnValueIdx++ ) { Object thisValue = returnValues[returnValueIdx]; jsonArray.put(toJSON(thisValue)); } jsonObj.put(propertyName, jsonArray); } else if( Hashtable.class.isAssignableFrom(returnType) ) { Hashtable map = (Hashtable)value; JSONObject jsonMap = new JSONObject(); Enumeration enumer = map.keys(); while( enumer.hasMoreElements() ) { Object key = (String)enumer.nextElement(); Object thisValue = (Object)map.get(key); if(isSimpleType(thisValue.getClass())) { jsonMap.put(key.toString(), handleSimpleType(returnType,thisValue)); } else { jsonMap.put(key.toString(), toJSON(thisValue)); } } jsonObj.put(propertyName, jsonMap); } else if( Vector.class.isAssignableFrom(returnType) ) { Vector returnValues = (Vector)property.getGetterSetter().getValue(obj); JSONArray jsonArray = new JSONArray(); int size = returnValues.size(); for( int returnValueIdx=0; returnValueIdx<size; returnValueIdx++ ) { Object thisValue = returnValues.elementAt(returnValueIdx); if(isSimpleType(property.getContainedType()) ) { jsonArray.put(thisValue); } else { jsonArray.put(toJSON(thisValue)); } } jsonObj.put(propertyName, jsonArray); } else { jsonObj.put(propertyName, toJSON(value)); } } } catch( Exception ite ) { throw new JSONException(ite); } } return jsonObj; } private Object handleSimpleType(Class returnType, Object value) { if( returnType.isArray() ) { Object[] returnValues = (Object[])value; JSONArray jsonArray = new JSONArray(); for( int returnValuesIdx=0; returnValuesIdx<returnValues.length; returnValuesIdx++ ) { Object thisValue = returnValues[returnValuesIdx]; jsonArray.put(thisValue); } return jsonArray; } else { return value; } } private Object[] toTypedArray(Vector list) { if( list.isEmpty() ) { return null; } Object first = list.elementAt(0); Object[] ret = null; if( first instanceof String ) { ret = new String[list.size()]; } else if( first instanceof Double ) { ret = new Double[list.size()]; } else if( first instanceof Integer ) { ret = new Integer[list.size()]; } else if( first instanceof Long ) { ret = new Long[list.size()]; } else if( first instanceof Boolean ) { ret = new Boolean[list.size()]; } list.copyInto(ret); return (Object[])ret; } private Object correctCasting(Class type, Object obj) { if( type.equals(Long.class) ) { return new Long(Long.parseLong(obj.toString())); } else if( type.equals(Double.class) ) { return Double.valueOf(obj.toString()); } else if( type.equals(Integer.class) ) { return Integer.valueOf(obj.toString()); } else return obj; } private boolean isSimpleType(Class returnType) { if(returnType.isArray()) { return Boolean[].class.isAssignableFrom(returnType) || Long[].class.isAssignableFrom(returnType) || Double[].class.isAssignableFrom(returnType) || Integer[].class.isAssignableFrom(returnType) || String[].class.isAssignableFrom(returnType); } else { return Boolean.class.isAssignableFrom(returnType) || Long.class.isAssignableFrom(returnType) || Double.class.isAssignableFrom(returnType) || Integer.class.isAssignableFrom(returnType) || String.class.isAssignableFrom(returnType); } } }