美文网首页
20171122-fastJson、序列化、深拷贝

20171122-fastJson、序列化、深拷贝

作者: 好奇的小刺猬 | 来源:发表于2017-12-22 21:00 被阅读1013次

    fastJson过滤器:

    fastjson 过滤不需要的字段或者只要某些字段

    /*
     * 第一种:在对象响应字段前加注解,这样生成的json也不包含该字段。
     * @JSONField(serialize=false)  
     * private String name;  
     */
    
    /*
     * 第二种:在对象对应字段前面加transient,表示该字段不用序列化,即在生成json的时候就不会包含该字段了。
     * private transient  String name;  
     */
    
    /*
     * 第三种:使用fastjson的拦截器
     * PropertyFilter profilter = new PropertyFilter(){  
      
                @Override  
                public boolean apply(Object object, String name, Object value) {  
                    if(name.equalsIgnoreCase("last")){  
                        //false表示last字段将被排除在外  
                        return false;  
                    }  
                    return true;  
                }  
                  
            };  
            json = JSON.toJSONString(user, profilter);  
            System.out.println(json);   
     */
    
    /*
     * 第四种,直接填写属性
     * SimplePropertyPreFilter filter = new SimplePropertyPreFilter(TTown.class, "id","townname");  
        response.getWriter().write(JSONObject.toJSONString(townList,filter));   
    
     */
    

    工具组同学在做热修复时发现一种robust不能兼容的情况。就是对对象进行序列化,这个对象会根据类的字段和方法自动生成serialVersionUID, robust插入代码后serialVersionUID变掉了,导致反序列化失败。
    因为这个整理了下相关文档。
    文档:

    既然如此,那没有定义serialVersionUID的那些对象,被存入的本地文件,新版本该类改变了类变量,那读取的旧缓存的文件还能正确恢复吗,新添加的字段被恢复成默认值还是直接报InvalidClassException的异常?

    那如果是通过fastJson的jsonString存储和恢复呢?因为这种方式是将对象编程键值对的json格式,不是严格意义上序列化,github的说明上也说了,只要是符合JavaBean规范的都可以,不用非要serialVerionUID。demo很简单就可以证明。

    具体详细的解释可以参考:Java序列化

    平时定义的bean都是json对应的,看样子不怎么符合JavaBean标准。而且如有有复杂对象的拷贝需求,fastJson的方法看起来是最方便最快的。

    正好需求中碰到相册数据的深拷贝,用的是序列化的方法做的。想到能不能改成json解析的方法,然后就刚好遇到了个问题。

    1. 序列化方法实现:单个对象clone,列表要在外层循环调用clone方法
        // serializable object
        public static GalleryItem deepCloneGalleryItem(GalleryItem in) {
            try {
                long ss= System.currentTimeMillis();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(in);
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                LogUtil.v("InLog", "success deepCloneGalleryItem= " + (System.currentTimeMillis() - ss)) ;
                return (GalleryItem) ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
                LogUtil.e("InLog", "IOException deepCloneGalleryItem");
                return null;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                LogUtil.e("InLog", "ClassNotFoundException deepCloneGalleryItem");
                return null;
            }
        }
    
    
        @Override
        public void notifyPrepareDataList(List<GalleryItem> list) {
            for (GalleryItem item : list) {
                it = ListUtil.deepCloneGalleryItem(item);
            }
        }
    
    1. Json转化方法实现:单个对象clone或直接整个列表clone
        public static void cloneJson(GalleryItem in){
            long ss = System.currentTimeMillis();
            String json = JSON.toJSONString(in);
            GalleryItem item = JSON.parseObject(json, GalleryItem.class);
            LogUtil.v("InLog", "cloneJson cost " + (System.currentTimeMillis() - ss));
            LogUtil.v("InLog", "cloneJson in " + in.hashCode() + ",item is  " + item.hashCode());
        }
    
        public static void cloneJson(List<GalleryItem> in){
            long ss = System.currentTimeMillis();
            String json = JSON.toJSONString(in);
            List<GalleryItem> item = JSON.parseArray(json, GalleryItem.class);
            LogUtil.d("InLog", "cloneJson cost " + (System.currentTimeMillis() - ss));
        }
        
    
        @Override
        public void notifyPrepareDataList(List<GalleryItem> list) {
            ListUtil.cloneJson(list);
        }
    

    该对象的以上两种方法,json转换的方法并没有更快。1000个对象时间差不多。

    使用的fastJson是android版本:'com.alibaba:fastjson:1.1.51.android',因为GalleryItem有个long型字段复写get方法时返回了String,导致JSON.toJSONString(in);一直报错:

    Error:
    com.alibaba.fastjson.JSONException: write javaBean error
    at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:600)
    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:262)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:607)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:334)
    ..............................................
    .....
    Caused by: java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
    at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:526)
    ... 21 more

    被这个问题困扰好久,app os:死前我都提示线索了,你怎么还没破案啊。
    原因就是get方法返回型和定义的不一样了。所以这种在bean里直接改变返回型的做法不适合在fastJson里用。

        public long duration;
        public String getDuration() {
            try {
                return formatTimeWithMin(duration);
            } catch (NumberFormatException e) {
                return "0:00";
            }
        }
    

    但是在使用新的版本compile com.alibaba:fastjson:1.1.67.android,以上不会出错。应该是修复了该问题,但是在JonsString解析成对象时还是会报类型转换的异常导致转换不成功。所以还是在使用时注意这种情况的转换。

    image

    附送其他使用注意事项fastjson使用过程中的坑

    以及一个容易被忽视的原因分析。
    JVM源码分析之不保证顺序的Class.getMethods

    当然开源库在更新,新的版本会有不同的优化,注意差异。

    相关文章

      网友评论

          本文标题:20171122-fastJson、序列化、深拷贝

          本文链接:https://www.haomeiwen.com/subject/enwxgxtx.html