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

org.apache.wink.json4j.internal.BeanSerializer Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.wink.json4j.internal;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.wink.json4j.JSONArray;
import org.apache.wink.json4j.JSONArtifact;
import org.apache.wink.json4j.JSONException;
import org.apache.wink.json4j.JSONObject;

/**
 * Private class to introspect a JavaBean and convert it to its respective JSON type.
 */
public class BeanSerializer {

    /** 
     * This method inspects a bean and converts it to its corrisponding JSON. 
     * This function expects non-basic types (no String, Number, etc).
     * @param obj The Object to inspect.
     * @param includeSuperclass Boolean indicating if superclass properties should be included in the output JSON.                       
     * @return An instance of the JSONArtifact that best represents the data in this JavaBean
     * @throws IllegalArgumentException Thrown if input type is a String, Number, Boolean, etc.
     * @throws JSONException Thrown if a JSON conversion error occurs.
     */
    public static JSONArtifact toJson(Object obj, boolean includeSuperclass) throws IllegalArgumentException, JSONException {
        JSONArtifact ja = null;

        if (obj != null) {
            Class clazz = obj.getClass();
            if (String.class  == clazz) {
                throw new IllegalArgumentException("Class was String type, not a Javabean.");
            } else if (Boolean.class == clazz) {
                throw new IllegalArgumentException("Class was Boolean type, not a Javabean.");
            } else if (Number.class.isAssignableFrom(clazz)) {
                throw new IllegalArgumentException("Class was Number type, not a Javabean.");
            } else if (JSONObject.class.isAssignableFrom(clazz)) {
                ja = (JSONObject)obj;
            } else if (JSONArray.class.isAssignableFrom(clazz)) {
                ja = (JSONArray)obj;
            } else if (Map.class.isAssignableFrom(clazz)) {
                ja = new JSONObject((Map)obj);
            } else if (Collection.class.isAssignableFrom(clazz)) {
                ja = new JSONArray((Collection)obj);
            } else if (clazz.isArray()) {
                ja = new JSONArray((Object[])obj);
            } 
            else {
                //TODO:  Bean introspection time.
                ja = introspectBean(obj,includeSuperclass, new ArrayList());
            }
        }
        return ja;
    }

    /**
     * Internal method for introspecting a bean and converting it to a JSONAble type.
     * @param obj The Object to inspect.
     * @param includeSuperclass Boolean indicating if superclass properties should be included in the output JSON.                       
     * @param parsedObjects An array list of objects traversed to try and avoid loops in graphs
     * @throws JSONException Thrown if a JSON conversion error occurs.
     */
    private static JSONArtifact introspectBean(Object obj, boolean includeSuperclass, ArrayList parsedObjects) throws JSONException {
        JSONObject ja = null;
        boolean found = false; 
        for (int i = 0; i < parsedObjects.size(); i++) {
            // Check and try to avoid graphs by parsing the same 
            // object multiple times, which may indicate a cycle.
            Object possibleObj = parsedObjects.get(i);
            if (possibleObj != null && obj == possibleObj) {
                found = true;
                break;
            }
        }

        if (!found) {
            parsedObjects.add(obj);
            ja = new JSONObject();

            Class clazz = obj.getClass();

            ja.put("_type", "JavaClass");
            ja.put("_classname", clazz.getName());

            // Fetch all the methods, based on including superclass or not.
            Method[] methods = null;
            if (includeSuperclass) {
                methods = clazz.getMethods();
            } else {
                methods = clazz.getDeclaredMethods();
            }

            if (methods != null && methods.length > 0) {
                for (int i = 0; i < methods.length; i++) {
                    Method m = methods[i];
                    // Include all superclass methods if requested, 
                    // or only those that are part of the actual declaring class.
                    String mName = m.getName();
                    Class[] types = m.getParameterTypes();

                    // Getter, so we can assume this accesses a field.
                    if (mName.startsWith("get") && mName.length() > 3 && (types == null || types.length == 0)) {
                        String attr = mName.substring(3, mName.length());
                        attr = Character.toLowerCase(attr.charAt(0)) + attr.substring(1, attr.length());
                        try {
                            Object val = m.invoke(obj);
                            if (val == null) {
                                ja.put(attr, (Object)null);
                            } else {
                                Class vClazz = val.getClass();
                                if (String.class == vClazz) {
                                    ja.put(attr, val);
                                } else if (Boolean.class == vClazz) {
                                    ja.put(attr, val);
                                } else if (Class.class == vClazz) {
                                    ja.put(attr, ((Class)val).getName());
                                } else if (Number.class.isAssignableFrom(vClazz)) {
                                    ja.put(attr, val);
                                } else if (JSONObject.class.isAssignableFrom(vClazz)) {
                                    ja.put(attr, val);
                                } else if (JSONArray.class.isAssignableFrom(vClazz)) {
                                    ja.put(attr, val);
                                } else if (Map.class.isAssignableFrom(vClazz)) {
                                    ja.put(attr, new JSONObject((Map)val));
                                } else if (Collection.class.isAssignableFrom(vClazz)) {
                                    ja.put(attr, new JSONArray((Collection)obj));
                                } else {
                                    if (val != obj) {
                                        // Try to avoid processing references to itself.
                                        ja.put(attr, introspectBean(val, includeSuperclass, parsedObjects));
                                    }
                                }
                            }
                        } catch (Exception ex) {
                            ja.put(attr, (Object)null);
                        }
                    }
                }
            }
        }
        return ja;
    }

    /**
     * Method to try to convert a JSONObject back into its class representation.
     * @param jo The JSONObject to try to convert back to a class.
     * @throws NullPointerException Thrown if jo is null.
     * @throws JSONException Thrown if the JSON cannot be converted to a java class.
     * @return An instance of a Java Object that corrisponds to the type in _classname
     */
    public static Object fromJson(JSONObject jo) throws NullPointerException, JSONException {
        Object obj = null;
        if (jo == null) {
            throw new NullPointerException("Input JSONObject cannot be null");
        } else {
            if (jo.get("_classname") != null && "JavaClass".equals(jo.get("_type"))) {
                // Okay, we can try to process this back to a class.
                try {
                    String cName = (String)jo.get("_classname");
                    Class clazz = Class.forName(cName);
                    if (clazz != null) {
                        Method[] methods = clazz.getMethods();

                        obj = clazz.newInstance();
                        Iterator keys = jo.keys();
                        if (keys != null) {
                            while (keys.hasNext()) {
                                String key = (String)keys.next();
                                // Ignore our specially named attributes.
                                if(key != null && !key.equals("_classname") && !key.equals("_type")){
                                    Method m = null;

                                    String setter = "set" + Character.toUpperCase(key.charAt(0)) + key.substring(1, key.length());
                                    Object val = jo.get(key);

                                    if (val != null) {
                                        Class vClazz = val.getClass();

                                        // Try to locate the best matching method.
                                        if (String.class == vClazz) {
                                            // Handle locating a String method.
                                            for (int i = 0; i < methods.length; i++) {
                                                Method tM = methods[i];
                                                if(tM.getName().equals(setter)){
                                                    Class[] mParms = tM.getParameterTypes();
                                                    if (mParms != null && mParms.length == 1) {
                                                        // Possible method, lets check the type.
                                                        Class c = mParms[0];
                                                        if (c == vClazz) {
                                                            // We have a String match, stop here.
                                                            m = tM;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        } else if (Boolean.class == vClazz) {
                                            // Handle locating a boolean method.
                                            for (int i = 0; i < methods.length; i++) {
                                                Method tM = methods[i];
                                                if(tM.getName().equals(setter)){
                                                    Class[] mParms = tM.getParameterTypes();
                                                    if (mParms != null && mParms.length == 1) {
                                                        // Possible method, lets check the type.
                                                        Class c = mParms[0];
                                                        if (c == vClazz || Boolean.TYPE == c) {
                                                            // We have a boolean match, stop here.
                                                            m = tM;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        } else if (Number.class.isAssignableFrom(vClazz)) {
                                            // Handle locating the best-matching number method.
                                            if(Double.class.isAssignableFrom(vClazz)){
                                                for (int i = 0; i < methods.length; i++) {
                                                    Method tM = methods[i];
                                                    if(tM.getName().equals(setter)){
                                                        Class[] mParms = tM.getParameterTypes();
                                                        if (mParms != null && mParms.length == 1) {
                                                            // Possible method, lets check the type.
                                                            Class c = mParms[0];
                                                            if (c == Double.class || Double.TYPE == c) {
                                                                // We have a double match, stop here.
                                                                m = tM;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a float assigner and if found, set the value to a Float
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Float.class || Float.TYPE == c) {
                                                                    // We have a Float match, stop here.
                                                                    m = tM;
                                                                    val = new Float(((Number)val).floatValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            } else if (Float.class.isAssignableFrom(vClazz)) {
                                                for (int i = 0; i < methods.length; i++) {
                                                    Method tM = methods[i];
                                                    if(tM.getName().equals(setter)){
                                                        Class[] mParms = tM.getParameterTypes();
                                                        if (mParms != null && mParms.length == 1) {
                                                            // Possible method, lets check the type.
                                                            Class c = mParms[0];
                                                            if (c == Float.class || Float.TYPE == c) {
                                                                // We have a Float match, stop here.
                                                                m = tM;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a double assigner and if found, set the value to a Float
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Double.class || Double.TYPE == c) {
                                                                    // We have a Double match, stop here.
                                                                    m = tM;
                                                                    val = new Double(((Number)val).doubleValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            } else if (Long.class.isAssignableFrom(vClazz)){
                                                for (int i = 0; i < methods.length; i++) {
                                                    Method tM = methods[i];
                                                    if(tM.getName().equals(setter)){
                                                        Class[] mParms = tM.getParameterTypes();
                                                        if (mParms != null && mParms.length == 1) {
                                                            // Possible method, lets check the type.
                                                            Class c = mParms[0];
                                                            if (c == Long.class || Long.TYPE == c) {
                                                                // We have a Long match, stop here.
                                                                m = tM;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a integer assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Integer.class || Integer.TYPE == c) {
                                                                    // We have an int match, stop here.
                                                                    m = tM;
                                                                    val = new Integer(((Number)val).intValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a short assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Short.class || Short.TYPE == c) {
                                                                    // We have a short match, stop here.
                                                                    m = tM;
                                                                    val = new Short(((Number)val).shortValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            } else if (Integer.class.isAssignableFrom(vClazz)){
                                                for (int i = 0; i < methods.length; i++) {
                                                    Method tM = methods[i];
                                                    if(tM.getName().equals(setter)){
                                                        Class[] mParms = tM.getParameterTypes();
                                                        if (mParms != null && mParms.length == 1) {
                                                            // Possible method, lets check the type.
                                                            Class c = mParms[0];
                                                            if (c == Integer.class || Integer.TYPE == c) {
                                                                // We have an int match, stop here.
                                                                m = tM;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a Long assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Long.class || Long.TYPE == c) {
                                                                    // We have a long match, stop here.
                                                                    m = tM;
                                                                    val = new Long(((Number)val).longValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a short assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Short.class || Short.TYPE == c) {
                                                                    // We have a short match, stop here.
                                                                    m = tM;
                                                                    val = new Short(((Number)val).shortValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            } else if (Short.class.isAssignableFrom(vClazz)){
                                                for (int i = 0; i < methods.length; i++) {
                                                    Method tM = methods[i];
                                                    if(tM.getName().equals(setter)){
                                                        Class[] mParms = tM.getParameterTypes();
                                                        if (mParms != null && mParms.length == 1) {
                                                            // Possible method, lets check the type.
                                                            Class c = mParms[0];
                                                            if (c == Short.class || Short.TYPE == c) {
                                                                // We have a short match, stop here.
                                                                m = tM;
                                                                break;
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a integer assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Integer.class || Integer.TYPE == c) {
                                                                    // We have a int match, stop here.
                                                                    m = tM;
                                                                    val = new Integer(((Number)val).intValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                                if(m == null){
                                                    // Look for a Long assigner and if found, set the value to a int
                                                    // type.
                                                    for (int i = 0; i < methods.length; i++) {
                                                        Method tM = methods[i];
                                                        if(tM.getName().equals(setter)){
                                                            Class[] mParms = tM.getParameterTypes();
                                                            if (mParms != null && mParms.length == 1) {
                                                                // Possible method, lets check the type.
                                                                Class c = mParms[0];
                                                                if (c == Long.class || Long.TYPE == c) {
                                                                    // We have a long match, stop here.
                                                                    m = tM;
                                                                    val = new Long(((Number)val).longValue());
                                                                    break;
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        } else if (JSONArray.class.isAssignableFrom(vClazz)) {
                                            // Handle determining a collection type to set, which means 
                                            // we need to find a Collection setter.
                                            for (int i = 0; i < methods.length; i++) {
                                                Method tM = methods[i];
                                                if(tM.getName().equals(setter)){
                                                    Class[] mParms = tM.getParameterTypes();
                                                    if (mParms != null && mParms.length == 1) {
                                                        // Possible method, lets check the type.
                                                        Class c = mParms[0];
                                                        if (List.class.isAssignableFrom(c)) {
                                                            // We have a Collections match, so we'll use it.
                                                            m = tM;
                                                            if(c != JSONArray.class){
                                                                // Convert it.  Whee.
                                                                List list = (List)c.newInstance();

                                                                JSONArray array = (JSONArray)val;
                                                                for (int j = 0; j < array.length(); j++) {
                                                                    // Convert each type as needed.
                                                                    Object aVal = array.get(j);
                                                                    if(aVal != null){
                                                                        Class aVClazz = aVal.getClass();
                                                                        if(Number.class.isAssignableFrom(aVClazz) ||
                                                                            Boolean.class.isAssignableFrom(aVClazz) ||
                                                                            String.class.isAssignableFrom(aVClazz)) {
                                                                            list.add(aVal);
                                                                        } else if (JSONObject.class.isAssignableFrom(aVClazz)) {
                                                                            list.add(fromJson((JSONObject)aVal));
                                                                        } else if (JSONObject.class.isAssignableFrom(aVClazz)) {
                                                                            // Not sure what to do here!
                                                                        }
                                                                    } else {
                                                                        list.add(null);
                                                                    }
                                                                }
                                                                val = list;
                                                            }
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        } else if (JSONObject.class.isAssignableFrom(vClazz)) {
                                            // Handle determining a map type to set, if there is one.
                                            JSONObject jObj = (JSONObject)val;
                                            Class vC = val.getClass();
                                            if(jObj.get("_classname") != null && "JavaClass".equals(jObj.get("_type"))){
                                                val = fromJson(jObj);
                                                vC = val.getClass();
                                            }

                                            // Handle locating a boolean method.
                                            for (int i = 0; i < methods.length; i++) {
                                                Method tM = methods[i];
                                                if(tM.getName().equals(setter)){
                                                    Class[] mParms = tM.getParameterTypes();
                                                    if (mParms != null && mParms.length == 1) {
                                                        // Possible method, lets check the type.
                                                        Class c = mParms[0];
                                                        if (c.isAssignableFrom(vC)) {
                                                            // We have setter for the conversion.
                                                            m = tM;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        } else {
                                            // Dunno?
                                            throw new JSONException("Unknown type: [" + vClazz.getName() + "]");
                                        }
                                    } else {
                                        try {
                                            m = clazz.getMethod(setter); // FIXME: [rfeng] This doesn't make much sense
                                        } catch (NoSuchMethodException nmex){
                                            // Ignore, no setter.
                                        }
                                    }
                                    if (m != null) {
                                        m.invoke(obj, new Object[] { val });
                                    }
                                }
                            }
                        }
                    } else {
                        throw new JSONException("Could not locate class: [" + cName + "]");
                    }
                } catch (Exception ex) {
                    if (ex instanceof JSONException) {
                        throw (JSONException)ex;
                    } else {
                        JSONException jex = new JSONException("Error in converting JSON to Java Class");
                        jex.initCause(ex);
                        throw jex;
                    }
                }
            } else {
                throw new JSONException("Provided JSONObject does not contain attributes '_classname' or '_type'");
            }
        }
        return obj;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy