org.json源码分析及增强(一)——JSONObject对象与Java对象相互转换

本系列文章将对org.json(https://github.com/stleary/JSON-java)的部分源代码进行分析,并介绍如何将其缺少的一些功能进行增强和完善。

为什么是org.json

Java世界中存在着各种针对JSON数据格式类型处理的各种利器,优秀者比如google-gson、Jackson、Genson、Flexjson等(可以从http://www.json.org/站点获取几乎所有编程语言的各种JSON支持项目)。 
org.json在其中简直可以说是默默无闻,但它非常轻量,几乎与json-simple一样轻,而且功能比较齐全,选择org.json进行分析和增强有利于我们对于相关知识的学习。以下是GitHub上org.json项目对于其源代码的说明:

JSONObject.java: The JSONObject can parse text from a String or a JSONTokener to produce a map-like object. The object provides methods for manipulating its contents, and for producing a JSON compliant object serialization.

JSONArray.java: The JSONObject can parse text from a String or a JSONTokener to produce a vector-like object. The object provides methods for manipulating its contents, and for producing a JSON compliant array serialization.

JSONTokener.java: The JSONTokener breaks a text into a sequence of individual tokens. It can be constructed from a String, Reader, or InputStream.

JSONException.java: The JSONException is the standard exception type thrown by this package.

JSONPointer.java: Implementation of JSON Pointer (RFC 6901). Supports 
JSON Pointers both in the form of string representation and URI fragment 
representation.

JSONString.java: The JSONString interface requires a toJSONString method,allowing an object to provide its own serialization.

JSONStringer.java: The JSONStringer provides a convenient facility for building JSON strings.

JSONWriter.java: The JSONWriter provides a convenient facility for building JSON text through a writer.

CDL.java: CDL provides support for converting between JSON and comma delimited lists.

Cookie.java: Cookie provides support for converting between JSON and cookies.

CookieList.java: CookieList provides support for converting between JSON and cookie lists.

HTTP.java: HTTP provides support for converting between JSON and HTTP headers.

HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers.

XML.java: XML provides support for converting between JSON and XML.

JSONML.java: JSONML provides support for converting between JSONML and XML.

XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text.

JSONObject.java源码分析

JSONObject是一个核心类,它内部利用HashMap进行存储,实现了从一个字符串、Map对象、Java对象,JSONObject对象自身来构建一个key-value形式的JSON数据格式类型对象(这种数据格式与JavaScript的对象格式非常相似)。从而达到Map一样的操作,并可以输出为标准的JSON字符串。 
在源码分析中我们主要讨论从字符串转换为JSONObject对象和从Java对象转换为JSONObject对象:

(1)字符串转换为JSONObject对象

public JSONObject(String source) throws JSONException {
        this(new JSONTokener(source));
    }

转换利用JSONTokener类,首先将字符串转换为可以将其按JSON数据格式拆解为一个一个循序的char对象的JSONTokener对象,之后遵循JSON数据格式的标准规则: 
1. 以”{“开头; 
2. 以”}”结尾; 
3. key后面紧跟着”:”; 
4. 属性对(key-value)之间使用”,”分割。 
利用以下代码,按JSONTokener对象所拆解的每个char对象一种顺序循环直至出现JSON语法错误或到达尾部终止,最终完成对HashMap的填充:

public JSONObject(JSONTokener x) throws JSONException {
        this();
        char c;
        String key;

        if (x.nextClean() != '{') {
            throw x.syntaxError("A JSONObject text must begin with '{'");
        }
        for (;;) {
            c = x.nextClean();
            switch (c) {
            case 0:
                throw x.syntaxError("A JSONObject text must end with '}'");
            case '}':
                return;
            default:
                x.back();
                key = x.nextValue().toString();
            }

// The key is followed by ':'.

            c = x.nextClean();
            if (c != ':') {
                throw x.syntaxError("Expected a ':' after a key");
            }
            this.putOnce(key, x.nextValue());

// Pairs are separated by ','.

            switch (x.nextClean()) {
            case ';':
            case ',':
                if (x.nextClean() == '}') {
                    return;
                }
                x.back();
                break;
            case '}':
                return;
            default:
                throw x.syntaxError("Expected a ',' or '}'");
            }
        }
    }

这里需要注意的是,在进行key和value取值时,使用了JSONTokener类的nextValue()方法,此方法将通过字符对类型进行判断,目前支持Boolean, Double, Integer,JSONArray, JSONObject, Long, String, JSONObject.NULL类型,主要的逻辑代码参考如下:

switch (c) {
            case '"':
            case '\'':
                return this.nextString(c);
            case '{':
                this.back();
                return new JSONObject(this);
            case '[':
                this.back();
                return new JSONArray(this);
        }

除去”外,\’对应一个String类型,{对应一个JSONObject类型,[对应一个JSONArray类型,对于”会继续判断,最终通过JSONObject.stringToValue()方法返回对应类型的Object对象。

(2)Java对象转换为JSONObject对象

public JSONObject(Object bean) {
        this();
        this.populateMap(bean);
    }

转换的实际是一个JavaBean对象,实际方法为populateMap,可以理解为是一个对于JavaBean对象实现序列化的通用方法。我们可以关注populateMap的几个细节。 
1. 通过getClassLoader()来判断处理的是否是一个纯系统(JRE)类或是一个基本类型,从JRS中我们可以看到这样一个描述:

Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader. 
If this object represents a primitive type or void, null is returned.

当启动一个JVM时,Bootstrap Classloader会加载java的核心类,也就是我们说的System Class,当你试图获取其类加载器时会返回一个null,因为Bootstrap Classloader并不是一个Java类,而是一个JVM的内部实现;另外,当获取基本类型的Classloader时也同样返回null。 
2. 通过Modifier.isPublic(method.getModifiers())判断是否为public方法,通过反射get方法获取对应的key值和value值,并利用wrap方法对value值判断类型后进行分别处理,wrap方法代码如下:

 public static Object wrap(Object object) {//核心方法:将目标对象包裹成一整个JSONObject中的value对象
        try {
            if (object == null) {
                return NULL;
            }
            //对于基本数据类型,直接进行使用
            if (object instanceof JSONObject || object instanceof JSONArray
                    || NULL.equals(object) || object instanceof JSONString
                    || object instanceof Byte || object instanceof Character
                    || object instanceof Short || object instanceof Integer
                    || object instanceof Long || object instanceof Boolean
                    || object instanceof Float || object instanceof Double
                    || object instanceof String || object instanceof BigInteger
                    || object instanceof BigDecimal || object instanceof Enum) {
                return object;
            }

            //对于集合类型将作为一个JSONArray
            if (object instanceof Collection) {
                Collection<?> coll = (Collection<?>) object;
                return new JSONArray(coll);
            }
            //对于一个数组类型将作为一个JSONArray
            if (object.getClass().isArray()) {
                return new JSONArray(object);
            }

            //对于一个Map类型
            if (object instanceof Map) {
                Map<?, ?> map = (Map<?, ?>) object;
                return new JSONObject(map);
            }


            Package objectPackage = object.getClass().getPackage();
            String objectPackageName = objectPackage != null ? objectPackage
                    .getName() : "";
            if (objectPackageName.startsWith("java.")
                    || objectPackageName.startsWith("javax.")
                    || object.getClass().getClassLoader() == null) {
                return object.toString();
            }
            return new JSONObject(object);
        } catch (Exception exception) {
            return null;
        }
    }

JSONObject对象与Java对象相互转换

org.json中的JSONObject类默认只支持将Java对象转换为JSONObject对象(这个可以从上文的源码分析中了解到),目前是不支持JSONObject对象到Java对象的转换的,因此,此章节将重点介绍我们如何来修改JSONObject类来增加JSONObject对象转换为Java对象的功能。

构建测试Java对象

需要构建一个相对复杂一点的测试对象,参考如下JavaBean对象代码:

public class Person {

    String name = null;
    int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class Student extends Person{
    String id = null;
    String[] course = null;
    int[] weightArray = null;
    Map<String, Object> map = null;
    ArrayList<Integer> scoreList = null;
    JSONObject json = null;
    Random rand = null;
    Clothes clothes = null;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String[] getCourse() {
        return course;
    }

    public void setCourse(String[] course) {
        this.course = course;
    }

    public int[] getWeightArray() {
        return weightArray;
    }

    public void setWeightArray(int[] weightArray) {
        this.weightArray = weightArray;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public ArrayList<Integer> getScoreList() {
        return scoreList;
    }

    public void setScoreList(ArrayList<Integer> scoreList) {
        this.scoreList = scoreList;
    }

    public JSONObject getJson() {
        return json;
    }

    public void setJson(JSONObject json) {
        this.json = json;
    }

    public Random getRand() {
        return rand;
    }

    public void setRand(Random rand) {
        this.rand = rand;
    }

    public Clothes getClothes() {
        return clothes;
    }

    public void setClothes(Clothes clothes) {
        this.clothes = clothes;
    }

    static class Clothes {
        String name;
        String type;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getType() {
            return type;
        }
        public void setType(String type) {
            this.type = type;
        }

    }
}

我们的测试类Student是一个相对复杂的JavaBean,首先继承自Person类,并且属性类型包含基础类型、包装类类型、集合类型、Map类型、数组类型、JSONObject类、自己定义的类、系统自带的类。

Java对象转换为JSONObject对象

这个不多介绍,就是利用前文所介绍的方法JSONObject(Object bean),参考如下代码:

    public static void main(String[] args){
        Student student = new Student();
        Map<String, Object> map = new HashMap<String, Object>();
        ArrayList<Integer> list = new ArrayList<Integer>();
        String[] course = new String[]{"A","B"};
        int[] weightArray = new int[]{170,175,180};
        map.put("a", 1);
        map.put("b", 1);
        list.add(100);
        list.add(90);
        list.add(85);
        Random rand = new Random();
        Clothes clothes = new Clothes();
        clothes.setName("short");
        clothes.setType("black");
        student.setName("liush");
        student.setAge(33);
        student.setId("2222");
        student.setMap(map);
        student.setCourse(course);
        student.setJson(new JSONObject("{\"a\":1}"));
        student.setScoreList(list);
        student.setWeightArray(weightArray);
        student.setRand(rand);
        student.setClothes(clothes);
        JSONObject json = new JSONObject(student);
        System.out.println(json.toString());
    }

运行结果为:

{"rand":"java.util.Random@5c647e05","scoreList":[100,90,85],"weightArray":[170,175,180],"name":"liush","course":["A","B"],"json":{"a":1},"id":"2222","map":{"a":1,"b":1},"clothes":{"name":"short","type":"black"},"age":33}

JSONObject对象转换为Java对象

JSONObject对象转换为Java对象同样面临着识别对象类型之后根据类型进行不同处理的问题,对于类型识别方面可以参考上文wrap方法源代码进行反逻辑,但不同的是无法通过instanceof进行判断,需要根据类全名和反射方法来判断,需要更加细致的划分。这里需要关注以下几个问题: 
1. 如何对Map和List类型进行判断? 
可以说在Java对象转换为JSONObject对象时,对于这类判断只需要instanceof就可以,但对于反射过来的类,需要利用isInterface()和getInterfaces()进行对原属性声明类型为Map对象或HashMap对象分开处理。 
2. 如何将Object数组对象转换为Java基本数据类型的数组对象? 
对于Java的包装类类型可以直接使用泛型设定所要转换的数组类型。

<T> T[] toArray(T[] a)

对于Java的基本类型考虑如下方法:

 /**
     * 处理从Object数组转换到Java基本类型数组
     * @param source Object数组
     * @param typeName 待转换的基本类型数组名称
     * @return
     */
    private Object toBaseTypeArrayObject(Object[] source, String typeName){

        Object target = null;
        int length = source.length;
        switch(typeName){

            case "byte[]" :{
                byte[] tmp = new byte[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (byte)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "char[]" :{
                char[] tmp = new char[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (char)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "short[]" :{
                short[] tmp = new short[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (short)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "int[]" :{
                int[] tmp = new int[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (int)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "long[]" :{
                long[] tmp = new long[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (long)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "boolean[]" :{
                boolean[] tmp = new boolean[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (boolean)source[i];
                }
                target = (Object)tmp;
                break;
            }
            case "float[]" :{
                float[] tmp = new float[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (float)source[i];
                }
                break;
            }
            case "double[]" :{
                double[] tmp = new double[length];
                for(int i = 0; i < length; i++){
                    tmp[i] = (double)source[i];
                }
            }
            default : return target;
        }

        return target;
    }

最终的toJavaBean方法和map方法参考如下代码:

 /**
     * 
     * @param klass 待转换Java对象的class
     * @return Java对象
     */
    public Object toJavaBean(Class<?> klass){
        Object object = null;
        try {
            object = klass.newInstance();
            for(Method method : klass.getMethods()){
                if (Modifier.isPublic(method.getModifiers())) {

                    String name = method.getName();
                    String key = "";
                    if (name.startsWith("set")) {
                        key = name.substring(3);
                    }
                    //格式化Key字符串
                    if (key.length() > 0
                            && Character.isUpperCase(key.charAt(0))
                            && method.getParameterTypes().length == 1) {
                        if (key.length() == 1) {
                            key = key.toLowerCase();
                        } else if (!Character.isUpperCase(key.charAt(1))) {
                            key = key.substring(0, 1).toLowerCase()
                                    + key.substring(1);
                        }

                       try {
                           map(object, get(key), method);
                       } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                       } catch (InvocationTargetException e) {
                        e.printStackTrace();
                       } catch (JSONException e) {
                        e.printStackTrace();
                       }
                    }
                }
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return object;
    }

    /**
     * 通过set方法,将value值赋值给对象target的对象属性
     * @param target Java对象
     * @param value 属性值
     * @param method set方法
     */
    private void map(Object target, Object value, Method method) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{

        Class<?> klass = method.getParameterTypes()[0];

        boolean isJDKLibClass = (klass.getClassLoader() == null) ? true : false;
        String packageName = this.getClass().getPackage().getName();
        String jsonObjectName = packageName + ".JSONObject";
        String jsonArrayName = packageName + ".JSONArray";
        String jsonStringName = packageName + ".JSONString";
        String className = klass.getName();
        String classSimpleName = klass.getSimpleName();

        if(isJDKLibClass || className.equals(jsonObjectName) || className.equals(jsonArrayName) || className.equals(jsonStringName)){

            //处理映射基本类型对象
            if (classSimpleName.equalsIgnoreCase("Byte") || classSimpleName.equalsIgnoreCase("Character")
                  || classSimpleName.equalsIgnoreCase("Short") || classSimpleName.equalsIgnoreCase("Integer")
                  || classSimpleName.equalsIgnoreCase("int") || classSimpleName.equalsIgnoreCase("char")
                  || classSimpleName.equalsIgnoreCase("Long") || classSimpleName.equalsIgnoreCase("Boolean")
                  || classSimpleName.equalsIgnoreCase("Float") || classSimpleName.equalsIgnoreCase("Double")
                  || classSimpleName.equalsIgnoreCase("String") || classSimpleName.equalsIgnoreCase("BigInteger")
                  || classSimpleName.equalsIgnoreCase("BigDecimal") || classSimpleName.equalsIgnoreCase("Enum")
                  || className.equals(jsonObjectName) || className.equals(jsonArrayName) || className.equals(jsonStringName)) {

                method.invoke(target, value);
                return;
            }

            //处理映射List和Map类型的对象
            if(klass.isInterface()){

                if(klass.getSimpleName().equals("List")){
                    List<?> coll = ((JSONArray)value).toList();
                    method.invoke(target, coll);
                    return;
                }

                if(klass.getSimpleName().equals("Map")){
                    Map<?, ?> map = ((JSONObject)value).toMap();
                    method.invoke(target, map);
                    return;
                }

            } else {
                for(Class<?> interF : klass.getInterfaces()){
                    if(interF.getSimpleName().equals("List")){
                        List<?> coll = ((JSONArray)value).toList();
                        method.invoke(target, coll);
                        return;
                    }
                    if(interF.getSimpleName().equals("Map")){
                        Map<?, ?> map = ((JSONObject)value).toMap();
                        method.invoke(target, map);
                        return;
                    }
                }
            }

            //处理映射数组对象
            if(klass.isArray()){
                Object array = null;
                switch(classSimpleName){
                    case "Byte[]" :{
                        array = ((JSONArray)value).toList().toArray(new Byte[0]);
                        break;
                    }
                    case "Character[]" :{
                        array = ((JSONArray)value).toList().toArray(new Character[0]);
                        break;
                    }
                    case "Short[]" :{
                        array = ((JSONArray)value).toList().toArray(new Short[0]);
                        break;
                    }
                    case "Integer[]" :{
                        array = ((JSONArray)value).toList().toArray(new Integer[0]);
                        break;
                    }
                    case "Long[]" :{
                        array = ((JSONArray)value).toList().toArray(new Long[0]);
                        break;
                    }
                    case "Boolean[]" :{
                        array = ((JSONArray)value).toList().toArray(new Boolean[0]);
                        break;
                    }
                    case "Float[]" :{
                        array = ((JSONArray)value).toList().toArray(new Float[0]);
                        break;
                    }
                    case "Double[]" :{
                        array = ((JSONArray)value).toList().toArray(new Double[0]);
                        break;
                    }
                    case "String[]" :{
                        array = ((JSONArray)value).toList().toArray(new String[0]);
                        break;
                    }
                    case "BigInteger[]" :{
                        array = ((JSONArray)value).toList().toArray(new BigInteger[0]);
                        break;
                    }
                    case "BigDecimal[]" :{
                        array = ((JSONArray)value).toList().toArray(new BigDecimal[0]);
                        break;
                    }
                    default : {
                        array = toBaseTypeArrayObject(((JSONArray)value).toList().toArray(), classSimpleName);
                        break;
                    }
                }
                method.invoke(target, array);
                return;
            }

        } else {//处理一般Java对象

            Object object = ((JSONObject)value).toJavaBean(klass);
            method.invoke(target, object);
        }
    }

于是我们就可以利用toJavaBean方法实现JSONObject对象转换为JavaBean对象(反序列化),参考如下代码:

public static void main(String[] args){

        String str = "{\"rand\":\"java.util.Random@5c647e05\",\"scoreList\":[100,90,85],"
                + "\"weightArray\":[170,175,180],\"name\":\"liush\",\"json\":{\"a\":1},"
                + "\"course\":[\"A\",\"B\"],\"id\":\"2222\",\"map\":{\"a\":1,\"b\":1},"
                + "\"clothes\":{\"name\":\"short\",\"type\":\"black\"},\"age\":33}";
        JSONObject json = new JSONObject(str);      

        Student student = (Student)json.toJavaBean(Student.class);
        System.out.println("name:" + student.getName());
        System.out.println("age:" + student.getAge());
        System.out.println("id:" + student.getId());
        System.out.println("map:" + student.getMap().toString());
        System.out.println("course[0]:" + student.getCourse()[0]);
        System.out.println("course[1]:" + student.getCourse()[1]);
        System.out.println("json:" + student.getJson().toString());
        System.out.println("scoreList:" + student.getScoreList().toString());
        System.out.println("weightArray[0]:" + student.getWeightArray()[0]);
        System.out.println("weightArray[1]:" + student.getWeightArray()[1]);
        System.out.println("weightArray[2]:" + student.getWeightArray()[2]);
        System.out.println("rand:" + student.getRand());
        System.out.println("clothes[name]:" + student.getClothes().getName());
        System.out.println("clothes[type]:" + student.getClothes().getType());
    }

运行结果如下: 
这里写图片描述

猜你喜欢

转载自blog.csdn.net/suyimin2010/article/details/81406813