20171122-fastJson、序列化、深拷贝

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变掉了,导致反序列化失败。
因为这个整理了下相关文档。
文档:

  • Fastjson中文翻译
  • Java对象的序列化与反序列化
  • 对象序列化为何要定义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 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 in){
        long ss = System.currentTimeMillis();
        String json = JSON.toJSONString(in);
        List item = JSON.parseArray(json, GalleryItem.class);
        LogUtil.d("InLog", "cloneJson cost " + (System.currentTimeMillis() - ss));
    }
    
    @Override
    public void notifyPrepareDataList(List 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、序列化、深拷贝)