Gson如何自定义Calendar序列化/反序列化


直接开干吧,假设有这么一个类:

    @Data
    @AllArgsConstructor
    class BeanSample {
        public Calendar birthday;

        @Override
        public String toString() {
            if (birthday == null) {
                return "birthday:null";
            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
            return sdf.format(birthday.getTime());
        }
    }

默认情况下,Gson序列化出来的结果很难看:

        Gson gson  = new Gson();
        BeanSample bean = new BeanSample(Calendar.getInstance());
        System.out.println(gson.toJson(bean));

输出:

{"birthday":{"year":2022,"month":3,"dayOfMonth":20,"hourOfDay":21,"minute":18,"second":13}}

以我多年的搬砖经验,加个TypeAdapter应该就可以了吧:

        Gson gson = new GsonBuilder().registerTypeAdapter(Calendar.class, new TypeAdapter() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

            @Override
            public void write(JsonWriter out, Calendar value) throws IOException {
                if (value == null) {
                    out.nullValue();
                } else {
                    out.value(sdf.format(value.getTime()));
                }
            }

            @Override
            public Calendar read(JsonReader in) throws IOException {
                //这是从json字符串反序列化的,先不管
                return null;
            }
        }).create();
        
        BeanSample bean = new BeanSample(Calendar.getInstance());
        System.out.println(gson.toJson(bean));

运行一下,居然没生效,还是刚才的格式!于是,下个断点:

发现birthday最终实例化后,类型是 GregorianCalendar(即:抽象类Calendar的子类),然后把刚才的代码略改了下:

Gson gson = new GsonBuilder().registerTypeAdapter(GregorianCalendar.class, new TypeAdapter() {

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    @Override
    public void write(JsonWriter out, GregorianCalendar value) throws IOException {
        if (value == null) {
            out.nullValue();
        } else {
            out.value(sdf.format(value.getTime()));
        }
    }

    @Override
    public GregorianCalendar read(JsonReader in) throws IOException {
        JsonToken token = in.peek();
        switch (token) {
            case NUMBER:
                Date d = new Date(in.nextLong());
                GregorianCalendar c = new GregorianCalendar();
                c.setTime(d);
                return c;
            case STRING:
                String s = in.nextString();
                try {
                    Date parse = sdf.parse(s);
                    GregorianCalendar instance = new GregorianCalendar();
                    instance.setTime(parse);
                    return instance;
                } catch (ParseException e) {
                    e.printStackTrace();
                    return null;
                }
            case NULL:
            default:
                return null;
        }
    }
}).create();


BeanSample bean = new BeanSample(new GregorianCalendar());

//序列化
String json = gson.toJson(bean);
System.out.println(json);

//反序列化
System.out.println(gson.fromJson(json, BeanSample.class));

System.out.println("--------");

//换成时间戳格式
json = "{\"birthday\":" + System.currentTimeMillis() + "}";
System.out.println(json);
System.out.println(gson.fromJson(json, BeanSample.class));

输出:

{"birthday":"2022-04-20 22:27:08.864"}
2022-04-20 22:27:08.864
--------
{"birthday":1650464828881}
2022-04-20 22:27:08.881

这样看起来好多了,而且json反序列化时,时间戳long型数字也一并做了兼容

注:gson的其它小技巧,可参考先前写的文章