json-lib反序列化Date类型问题

好久没有写过博客,记录一下最近遇到的坑吧

项目有人使用了 json-lib 库进行反序列化对象,其他的值是能正确的反序列化,但是 long 类型的时间戳却不能被反序列化,虽然 Date 类型的字段会有值,但它的值是当前时间,我们来看一段代码。

假设有一个类 Response 有一个 Date 类型的字段 date :

public class Response {

    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

有了这个类我们来尝试讲一段 json 串反序列化一下:

public static void main(String[] args) {
    // 这里一个时间戳,为 2011-01-01 01:01:01
    String json = "{\"date\":1293814861000}";

    JSONObject jsonObject = JSONObject.fromObject(json);
    Response o = (Response) JSONObject.toBean(jsonObject, Response.class);
    System.out.println(o.getDate());
}

我们运行一下这段代码,预期控制台应该打印 Sat Jan 01 01:01:01 CST 2011 ,然而它并没有,它打印了当前时间。这是为什么呢?因为 json-lib 库默认无法实现从 long 类型转为 Date 类型,它把 Date 当成了普通Java对象来处理了,通过默认的构造方法创建了实例,却没有做任何操作,导致 Date 类型的字段值错误。

如何解决这个问题呢?我们需要注册一个 Morpher 对象,这个对象描述如何将 long 类型值转为 Date 类型值,就是一个转换器的功能。来看一下这个类是怎样实现的:

import java.util.Date;

import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
import net.sf.ezmorph.MorphException;
import net.sf.ezmorph.object.AbstractObjectMorpher;
import net.sf.json.util.JSONUtils;

/**
 * @author
 */
@Slf4j
@Component
public class SimpleDateMorpher extends AbstractObjectMorpher {

    static {
        JSONUtils.getMorpherRegistry().registerMorpher(new SimpleDateMorpher());
    }

    /**
     * 标注 Morpher 支持哪种类型的转换
     */
    @Override
    public Class morphsTo() {
        return Date.class;
    }

    /**
     * 将 json 值转为 Date 类型的值
     * @param value json中的值
     */
    @Override
    public Object morph(Object value) {
        if (value == null) {
            return null;
        }

        if (Date.class.isAssignableFrom(value.getClass())) {
            return value;
        }

        if (!supports(value.getClass())) {
            throw new MorphException(value.getClass() + " is not supported");
        }

        try {
            if (value instanceof Long) {
                return new Date((Long) value);
            } else if (value instanceof String) {
                return DateUtils.formatToDate((String) value, "yyyy-MM-dd hh:mm:ss");
            }
        } catch (Exception e) {
            log.error("转换错误 {}", value, e);
        }

        // unable to parse the date
        throw new MorphException("Unable to parse the date " + value);
    }
}

这是一个很简单的功能,就是识别 long 类型和 String 类型值转为 Date 类型的值,其中 String 转的还限定了日期字符串的格式。
只要有了这个类,我们便可以让 json-lib 正确获取 Date 的值。

可能有的同学已经发现了,我在这个类中使用 @Component 和 static 块,这是用于在 Spring 环境下,进行自动的初始化 SimpleDateMorpher 类,注册只需要一次即可,不需要每次使用重新注册。

JSONUtils.getMorpherRegistry().registerMorpher(new SimpleDateMorpher());

这一串代码即表示在 json-lib 中注册 Date 类型的转换器。实际上 json-lib 也是有实现 Date 类型的 Morpher 类的,它就是 DateMorpher ,它这个类相比较我们的是复杂一点,它支持字符串形式的转换,完整的包括了国际化的问题,相比我们的而言,它只有不支持数值类型的转换一个缺点。

我们再来试一下反序列化的代码,我们增加一行注册的代码:

public static void main(String[] args) {
    // 这里一个时间戳,为 2011-01-01 01:01:01
    String json = "{\"date\":1293814861000}";

    DateMorpher dateMorpherClass = new DateMorpher();
    JSONObject jsonObject = JSONObject.fromObject(json);
    Response o = (Response) JSONObject.toBean(jsonObject, Response.class);
    System.out.println(o.getDate());
}

如此便可以实现 long 和 String 值转为 Date 的功能,如有需要可自行扩展功能。


这个坑还是相对隐蔽的,一般比较难发现,希望对各位有所帮助,如果文章有不当之处,欢迎指正。


魏晋秋
47 声望2 粉丝

勿惧勿怕