• Java中对象JSON格式化处理时的一个坑


    在项目中遇到了一个JSON的坑。记录下。

    直接上代码:

    import java.util.ArrayList;
    
    import com.alibaba.fastjson.JSON;
    
    public class MyList<E> extends ArrayList<E> {
        private int size;
        private String specialName;
        
        public MyList(){
            super(0);
        }
        
        public MyList(int size){
            super(0);
            this.size = size;
        }
        public MyList(String specialName){
            super(0);
            this.specialName = specialName;
        }
        public MyList(int size, String specialName){
            super(0);
            this.size = size;
            this.specialName = specialName;
        }
        public int getSize() {
            return size;
        }
    
        public void setSize(int size) {
            this.size = size;
        }
    
        public String getSpecialName() {
            return specialName;
        }
    
        public void setSpecialName(String specialName) {
            this.specialName = specialName;
        }
    
        public static void main(String[] args){
            MyList<Integer> list = new MyList<Integer>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            list.setSpecialName("just a test");
            list.setSize(4);
            System.out.println(JSON.toJSON(list));
            System.out.println(JSON.toJSONString(list));
        }
    }

    输出的结果为:

    [1,2,3,4]
    [1,2,3,4]

    但是我们期望的结果却是类似于下面这样的结果:

    {size:4, specialName:"just a test", [1,2,3,4]}

    那么是哪里出问题了呢?导致 MyList的 size 属性和 specialName 在JSON格式化时,被丢弃了呢

    下面在看一个例子:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import com.alibaba.fastjson.JSON;
    
    public class MyList2<E> {
        private int size;
        private String specialName;
        private List<E> list;
        
        public MyList2(){
            
        }
    
        public int getSize() {
            return size;
        }
    
        public void setSize(int size) {
            this.size = size;
        }
    
        public String getSpecialName() {
            return specialName;
        }
    
        public void setSpecialName(String specialName) {
            this.specialName = specialName;
        }
    
        public List<E> getList() {
            return list;
        }
    
        public void setList(List<E> list) {
            this.list = list;
        }
        
        public static void main(String[] args){
            MyList2<Integer> myList = new MyList2<Integer>();
            ArrayList<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            
            myList.setSpecialName("just a test");
            myList.setSize(4);
            myList.setList(list);
            System.out.println(JSON.toJSON(myList));
            System.out.println(JSON.toJSONString(myList));
            
            System.out.println("----------------");
            
            Map<String, Object> map = new HashMap<>();
            map.put("size", 4);
            map.put("specialName", "just a test");
            map.put("list", list);
            map.put("myList", myList);
            System.out.println(JSON.toJSON(map));
        }
    }

    输出的结果为:

    {"list":[1,2,3,4],"size":4,"specialName":"just a test"}
    {"list":[1,2,3,4],"size":4,"specialName":"just a test"}
    ----------------
    {"specialName":"just a test","size":4,"list":[1,2,3,4],"myList":{"list":[1,2,3,4],"size":4,"specialName":"just a test"}}

    结果完全正确。

    到这里,我们应该可以知道什么原因了。

    上面第一段代码我们期望的结果:

    {size:4, specialName:"just a test", [1,2,3,4]}

    但是想一想,这个格式有什么问题吗?仔细想一想

    FK,这个格式是完全错误的!他是不合法的。试想一下,JSON格式说白了,它是 Javascript 的一个子集,合法的json对象它是 javascript 中的对象,但是:

    {size:4, specialName:"just a test", [1,2,3,4]}

    他是一个合法的javascript对象吗????

    显然,它不是的。因为其中的 [1,2,3,4] ,我们无法访问,它没有对应到一个属性所以我们无法访问它。所以他不是一个合法的javascript对象,显然也就更加不是一个合法的json对象了。所以第一个例子,输出的结果:[1,2,3,4] 其实是可以接受的。

    是我们自己想要将一个不适合转化成JSON格式的Java对象强制转化成一个json格式,所以也就难免出现意料之外的情况了。那么 [1,2,3,4] 这个结果是如何来的呢。经过调试,其中的原因是,因为MyList继承自ArrayList,所以在JSON格式化时,就是将其作为了一个List或者说数组来处理的,我们看下相关源码:

        public static final Object toJSON(Object javaObject) {
            return toJSON(javaObject, ParserConfig.getGlobalInstance());
        }
    
        @SuppressWarnings("unchecked")
        public static final Object toJSON(Object javaObject, ParserConfig mapping) {
            if (javaObject == null) {
                return null;
            }
    
            if (javaObject instanceof JSON) {
                return (JSON) javaObject;
            }
    
            if (javaObject instanceof Map) {
                Map<Object, Object> map = (Map<Object, Object>) javaObject;
    
                JSONObject json = new JSONObject(map.size());
    
                for (Map.Entry<Object, Object> entry : map.entrySet()) {
                    Object key = entry.getKey();
                    String jsonKey = TypeUtils.castToString(key);
                    Object jsonValue = toJSON(entry.getValue());
                    json.put(jsonKey, jsonValue);
                }
    
                return json;
            }
    
            if (javaObject instanceof Collection) {
                Collection<Object> collection = (Collection<Object>) javaObject;
    
                JSONArray array = new JSONArray(collection.size());
    
                for (Object item : collection) {
                    Object jsonValue = toJSON(item);
                    array.add(jsonValue);
                }
    
                return array;
            }

    我们的MyList的对象,在 if (javaObject instanceof Collection) 处为true,所以被当做了JSONArray 来处理的每次处理JSONArray 中的一项,所以显然就不会处理到 size属性和specialName属性了,所以这两个属性被抛弃了。这是 JSON.toJSON() 的处理过程。

    下面看下 JSON.toJSONString(),其实也是一样,当成了数组或者说List来处理了:

        public ObjectSerializer getObjectWriter(Class<?> clazz) {
            ObjectSerializer writer = get(clazz);
    
            if (writer == null) {
                try {
                    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                    for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
                        if (!(o instanceof AutowiredObjectSerializer)) {
                            continue;
                        }
    
                        AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                        for (Type forType : autowired.getAutowiredFor()) {
                            put(forType, autowired);
                        }
                    }
                } catch (ClassCastException ex) {
                    // skip
                }
    
                writer = get(clazz);
            }
    
            if (writer == null) {
                final ClassLoader classLoader = JSON.class.getClassLoader();
                if (classLoader != Thread.currentThread().getContextClassLoader()) {
                    try {
                        for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
    
                            if (!(o instanceof AutowiredObjectSerializer)) {
                                continue;
                            }
    
                            AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
                            for (Type forType : autowired.getAutowiredFor()) {
                                put(forType, autowired);
                            }
                        }
                    } catch (ClassCastException ex) {
                        // skip
                    }
    
                    writer = get(clazz);
                }
            }
    
            if (writer == null) {
                if (Map.class.isAssignableFrom(clazz)) {
                    put(clazz, MapSerializer.instance);
                } else if (List.class.isAssignableFrom(clazz)) {
                    put(clazz, ListSerializer.instance);

    上面的代码 else if (List.class.isAssignableFrom(clazz)) 处为 true,所以其实是使用了 ListSerializer.instance 来处理了 MyList 的对象的,也就是当做了java.util.List 来处理的,所以自然就抛弃了 size属性和specialName属性。和上面的原理是一样的。

    总结下

    1)MyList这样继承自List的对象,并且有扩展属性的,它在json格式中是不能十分恰当的表示的,它会被当成List来处理,从而抛弃其它非List的属性。

    2)避免方法就是使用组合代替继承,或者使用Map,java.util.Map 天生就和json格式是最合适的,其实javascript中的对象,其实在某种程度上就是一个Map而已,属性名就是map中的key, 属性的值就是map的value。

    3)JSON格式介绍参见:http://www.cnblogs.com/digdeep/p/4572662.html

  • 相关阅读:
    UIKit框架-高级控件:2.UIScrollView的单图分页设置
    欢迎使用CSDN-markdown编辑器
    UIKit框架-高级控件:1.UIScrollView的基本认识
    UIKit基础:18-MVC模式, 代理消息传递机制, 计时器机制
    UIKit基础:17-基础控件的总结
    UIKit基础:16-小游戏之舒尔特表
    UIKit基础:15-与电脑玩剪刀石头布
    iOS之 Category 属性 的理解
    学习方法,特别对于新手
    iOS 之GCD串行和并发队列的理解
  • 原文地址:https://www.cnblogs.com/digdeep/p/4596458.html
Copyright © 2020-2023  润新知