org.protorabbit.json.DefaultSerializer.java Source code

Java tutorial

Introduction

Here is the source code for org.protorabbit.json.DefaultSerializer.java

Source

/*
* Protorabbit
*
* Copyright (c) 2009 Greg Murray (protorabbit.org)
*
* Licensed under the MIT License:
*
*  http://www.opensource.org/licenses/mit-license.php
*
*/

package org.protorabbit.json;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class DefaultSerializer implements JSONSerializer {

    private static Logger logger = null;

    static Logger getLogger() {
        if (logger == null) {
            logger = Logger.getLogger("org.protrabbit");
        }
        return logger;
    }

    public Object serialize(Object o) {

        // null is null
        if (o == null) {
            return JSONObject.NULL;
        }

        // collections
        if (Collection.class.isAssignableFrom(o.getClass())) {
            Iterator<?> it = ((Collection<?>) o).iterator();

            JSONArray ja = new JSONArray();
            while (it.hasNext()) {

                Object i = serialize(it.next());
                ja.put(i);
            }
            return ja;
        }

        // maps
        if (Map.class.isAssignableFrom(o.getClass())) {
            JSONObject jo = new JSONObject();
            Map<?, ?> m = ((Map<?, ?>) o);
            Iterator<?> ki = m.keySet().iterator();
            while (ki.hasNext()) {
                Object key = ki.next();
                Object value = serialize(m.get(key));
                try {
                    jo.put(key.toString(), value);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            return jo;
        }

        // primitives
        if (o instanceof Double || o instanceof Number || o instanceof Integer || o instanceof String
                || o instanceof Enum<?> || o instanceof Boolean) {
            return o;
        }

        if (o instanceof Date) {
            return ((Date) o).getTime();
        }

        // convert arrays to collections
        boolean b = o.getClass().isArray();

        if (b) {
            try {
                Object[] objs = (Object[]) o;
                List<Object> l = Arrays.asList(objs);
                return serialize(l);
            } catch (ClassCastException e) {
                return JSONObject.NULL;
            }
        }
        // serialize using bean like methods
        return serializePOJO(o, true);

    }

    protected boolean skipSerialization(Method m) {
        boolean skipIt = !(Modifier.isPublic(m.getModifiers()) && !"getHibernateLazyInitializer".equals(m.getName())
                && !"getClass".equals(m.getName()) && !"getParent".equals(m.getName())
                && !"getSystemClassLoader".equals(m.getName()) && !"getMethods".equals(m.getName())
                && !"getDeclaredClasses".equals(m.getName()) && !"getConstructors".equals(m.getName())
                && !"getDeclaringClass".equals(m.getName()) && !"getEnclosingClass".equals(m.getName())
                && !"getClassLoader".equals(m.getName())
                && (m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getName().length() > 2
                && m.getParameterTypes().length == 0);
        if (!skipIt && m.isAnnotationPresent(Serialize.class)) {
            Serialize s = m.getAnnotation(Serialize.class);

            if ("skip".equals(s.value())) {
                skipIt = true;
            }
        }
        return skipIt;
    }

    /*
     * Look at all the public methods in the object
     * find the ones that start with "get"
     *
     * create a property key for the methods and invoke the method using reflection
     * to get value.
     */
    public Object serializePOJO(Object pojo, boolean includeSuper) {

        if ("java.lang.Class".equals(pojo.getClass().getName())) {
            return null;
        }
        if (pojo.getClass().getClassLoader() == null) {
            includeSuper = false;
        }
        Object[] args = {};
        HashMap<String, Object> map = new HashMap<String, Object>();

        Method[] methods = null;
        if (includeSuper) {
            methods = pojo.getClass().getMethods();
        } else {
            methods = pojo.getClass().getDeclaredMethods();
        }

        for (int i = 0; i < methods.length; i++) {

            Method m = methods[i];
            try {

                // skip if there is a skip annotation
                if (!skipSerialization(m)) {
                    // change the case of the property from camelCase
                    String key = "";
                    if (m.getName().startsWith("is") && m.getName().length() > 3) {
                        key += m.getName().substring(2, 3).toLowerCase();
                        // get the rest of the name;
                        key += m.getName().substring(3);
                    } else if (m.getName().startsWith("get") && m.getName().length() >= 4) {
                        key += m.getName().substring(3, 4).toLowerCase();
                        // get the rest of the name;
                        key += m.getName().substring(4);
                    }
                    Object value = m.invoke(pojo, args);
                    map.put(key, value);
                }
            } catch (IllegalArgumentException e) {
                getLogger().warning("Unable to serialize " + pojo + " : " + e);
            } catch (IllegalAccessException e) {
                getLogger().warning("Unable to serialize " + pojo + " : " + e);
            } catch (InvocationTargetException e) {
                getLogger().warning("Unable to serialize " + pojo + " : " + e);
            }
        }
        // use the serializer itself to serialize a map of properties we created
        if (map.keySet().size() > 0) {
            return serialize(map);
        }

        return JSONObject.NULL;
    }

    /*
     * Get a 1 argument method matching the name and assignable with a given property
     */
    @SuppressWarnings("unchecked")
    void invokeMethod(Method[] methods, String key, String name, JSONObject jo, Object targetObject) {
        Object param = null;
        Throwable ex = null;
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName().equals(name)) {
                Class<?>[] paramTypes = m.getParameterTypes();
                if (paramTypes.length == 1 && jo.has(key)) {
                    Class<?> tparam = paramTypes[0];
                    boolean allowNull = false;
                    try {
                        if (jo.isNull(key)) {
                            // do nothing because param is already null : lets us not null on other types
                        } else if (Long.class.isAssignableFrom(tparam) || tparam == long.class) {
                            param = new Long(jo.getLong(key));
                        } else if (Double.class.isAssignableFrom(tparam) || tparam == double.class) {
                            param = new Double(jo.getDouble(key));
                        } else if (Integer.class.isAssignableFrom(tparam) || tparam == int.class) {
                            param = new Integer(jo.getInt(key));
                        } else if (String.class.isAssignableFrom(tparam)) {
                            param = jo.getString(key);
                        } else if (Enum.class.isAssignableFrom(tparam)) {
                            param = Enum.valueOf((Class<? extends Enum>) tparam, jo.getString(key));
                        } else if (Boolean.class.isAssignableFrom(tparam)) {
                            param = new Boolean(jo.getBoolean(key));
                        } else if (jo.isNull(key)) {
                            param = null;
                            allowNull = true;
                        } else if (Collection.class.isAssignableFrom(tparam)) {

                            if (m.getGenericParameterTypes().length > 0) {
                                Type t = m.getGenericParameterTypes()[0];
                                if (t instanceof ParameterizedType) {
                                    ParameterizedType tv = (ParameterizedType) t;
                                    if (tv.getActualTypeArguments().length > 0
                                            && tv.getActualTypeArguments()[0] == String.class) {

                                        List<String> ls = new ArrayList<String>();
                                        JSONArray ja = jo.optJSONArray(key);
                                        if (ja != null) {
                                            for (int j = 0; j < ja.length(); j++) {
                                                ls.add(ja.getString(j));
                                            }
                                        }
                                        param = ls;
                                    } else if (tv.getActualTypeArguments().length == 1) {
                                        ParameterizedType type = (ParameterizedType) tv.getActualTypeArguments()[0];
                                        Class itemClass = (Class) type.getRawType();
                                        if (itemClass == Map.class && type.getActualTypeArguments().length == 2
                                                && type.getActualTypeArguments()[0] == String.class
                                                && type.getActualTypeArguments()[1] == Object.class) {

                                            List<Map<String, Object>> ls = new ArrayList<Map<String, Object>>();

                                            JSONArray ja = jo.optJSONArray(key);
                                            if (ja != null) {
                                                for (int j = 0; j < ja.length(); j++) {
                                                    Map<String, Object> map = new HashMap<String, Object>();
                                                    JSONObject mo = ja.getJSONObject(j);
                                                    Iterator<String> keys = mo.keys();
                                                    while (keys.hasNext()) {
                                                        String okey = keys.next();
                                                        Object ovalue = null;
                                                        // make sure we don't get JSONObject$Null
                                                        if (!mo.isNull(okey)) {
                                                            ovalue = mo.get(okey);
                                                        }
                                                        map.put(okey, ovalue);
                                                    }
                                                    ls.add(map);
                                                }
                                            }
                                            param = ls;
                                        } else {
                                            getLogger().warning(
                                                    "Don't know how to handle Collection of type : " + itemClass);
                                        }

                                    } else {
                                        getLogger().warning("Don't know how to handle Collection of type : "
                                                + tv.getActualTypeArguments()[0]);
                                    }
                                }
                            }
                        } else {
                            getLogger().warning(
                                    "Unable to serialize " + key + " :  Don't know how to handle " + tparam);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    if (param != null || allowNull) {

                        try {

                            if (m != null) {
                                Object[] args = { param };
                                m.invoke(targetObject, args);
                                ex = null;
                                break;
                            }
                        } catch (SecurityException e) {
                            ex = e;
                        } catch (IllegalArgumentException e) {
                            ex = e;
                        } catch (IllegalAccessException e) {
                            ex = e;
                        } catch (InvocationTargetException e) {
                            ex = e;
                        }
                    }
                }
            }
        }
        if (ex != null) {
            if (ex instanceof RuntimeException) {
                throw (RuntimeException) ex;
            } else {
                throw new RuntimeException(ex);
            }
        }
    }

    public void deSerialize(String jsonText, Object targetObject) {
        try {
            JSONObject jo = new JSONObject(jsonText);
            deSerialize(jo, targetObject);
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

    @SuppressWarnings("unchecked")
    public void deSerialize(Object jsonObject, Object targetObject) {

        if (jsonObject != null) {
            JSONObject jo = (JSONObject) jsonObject;
            Method[] methods = targetObject.getClass().getMethods();
            Iterator<String> it = jo.keys();
            while (it.hasNext()) {
                String key = it.next();
                // jump out if the key length is too short
                if (key.length() <= 1)
                    continue;
                String mName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
                invokeMethod(methods, key, mName, jo, targetObject);
            }
        }
    }

    public Object genericDeserialize(String jsonText) {
        // check for array
        jsonText = jsonText.trim();
        if (jsonText.startsWith("[")) {

            try {
                JSONArray ja = new JSONArray(jsonText);
                return genericDeserialize(ja, null, null);
            } catch (JSONException jex) {
                throw new RuntimeException("Error Parsing JSON:" + jex);
            }
            // check for object
        } else if (jsonText.startsWith("{")) {
            try {
                JSONObject jo = new JSONObject(jsonText);
                return genericDeserialize(jo, null, null);
            } catch (JSONException jex) {
                throw new RuntimeException("Error Parsing JSON:" + jex);
            }
        } else {
            throw new RuntimeException("Error : Can only deserialize JSON objects or JSON arrays");
        }
    }

    @SuppressWarnings("unchecked")
    private void addValue(Object targetObject, Object value, String key) {
        if (targetObject == null) {
            throw new RuntimeException("Error serializing: Can only deserialize JSON objects or JSON arrays");
        } else if (targetObject instanceof ArrayList) {
            List l = (List) targetObject;
            l.add(value);
        } else if (targetObject instanceof HashMap && key != null) {
            Map m = (Map) targetObject;
            m.put(key, value);
        }
    }

    @SuppressWarnings("unchecked")
    private Object genericDeserialize(Object o, Object targetObject, String key) throws JSONException {

        if (o instanceof JSONObject) {
            JSONObject jo = (JSONObject) o;
            Map<String, Object> jaoo = new HashMap<String, Object>();

            // only add if we are not top level
            if (targetObject != null) {
                addValue(targetObject, jaoo, key);
            }
            Iterator<String> it = jo.keys();

            while (it.hasNext()) {
                String k = it.next();
                Object value = jo.get(k);
                genericDeserialize(value, jaoo, k);
            }
            // if we are the top level object return self
            if (targetObject == null) {
                return jaoo;
            }

        } else if (o instanceof JSONArray) {

            JSONArray ja = (JSONArray) o;

            List<Object> jal = new ArrayList<Object>();

            // only add if we are not top level
            if (targetObject != null) {
                addValue(targetObject, jal, key);
            }
            for (int i = 0; i < ja.length(); i++) {
                Object jao = ja.get(i);
                genericDeserialize(jao, jal, null);
            }
            // if we are the top level object return self
            if (targetObject == null) {
                return jal;
            }
            // primitives
        } else if (o instanceof Date) {
            Object value = ((Date) o).getTime();
            addValue(targetObject, value, key);
        } else {
            addValue(targetObject, o, key);
        }
        return null;
    }

    public Object deSerialize(String jsonText, Class<?> targetClass) {
        if (jsonText == null) {
            return null;
        }
        // check for array
        jsonText = jsonText.trim();
        if (jsonText.startsWith("[")) {
            try {
                JSONArray ja = new JSONArray(jsonText);

                List<Object> jal = new ArrayList<Object>();

                for (int i = 0; i < ja.length(); i++) {
                    try {
                        Object jao = ja.get(i);
                        Object targetObject = targetClass.newInstance();
                        deSerialize(jao, targetObject);
                        jal.add(targetObject);
                    } catch (InstantiationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }
                return jal;
            } catch (JSONException jex) {
                throw new RuntimeException("Error Parsing JSON:" + jex);
            }
            // check for object
        } else if (jsonText.startsWith("{")) {
            try {
                Object o = targetClass.newInstance();
                deSerialize(jsonText, o);
                return o;
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            throw new RuntimeException("Error : Can only deserialize JSON objects or JSON arrays");
        }
        return null;
    }

    public static void main(String[] args) {
        /*
           DefaultSerializer df = new DefaultSerializer();
           TestObject to = new TestObject();
           to.setFirstName("Greg");
           to.setLastName("Murray");
           to.setTimeout(55L);
           to.setFoo( TestObject.Foo.One);
           System.out.println("Original Object=" + to);
           JSONObject json = (JSONObject) df.serialize(to);
           System.out.println("JSON Object=" + json);
           TestObject to2 = (TestObject) df.deSerialize(json.toString(), TestObject.class);
           System.out.println("After=" + to2);
           */
    }
}