[alibaba/fastjson]JsonObject转换为JavaBean时LocalDateTime类型字段有些情况会转换失败

2025-10-28 495 views
5

时间字符串为 "09/13/2019 13:08:53" 时可以转换成功, 为"09/11/2019 13:08:53"时转换失败

异常信息如下:

Exception in thread "main" com.alibaba.fastjson.JSONException: Text '09/11/2019 13:08:53' could not be parsed at index 0 at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:1378) at com.alibaba.fastjson.JSONObject.toJavaObject(JSONObject.java:599) at TestEntity.main(TestEntity.java:26) Caused by: com.alibaba.fastjson.JSONException: Text '09/11/2019 13:08:53' could not be parsed at index 0 at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:687) at com.alibaba.fastjson.JSON.parseObject(JSON.java:383) at com.alibaba.fastjson.JSON.parseObject(JSON.java:287) at com.alibaba.fastjson.JSON.parseObject(JSON.java:560) at com.alibaba.fastjson.util.TypeUtils.cast(TypeUtils.java:1062) at com.alibaba.fastjson.util.TypeUtils.cast(TypeUtils.java:1134) at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.createInstance(JavaBeanDeserializer.java:1405) at com.alibaba.fastjson.util.TypeUtils.castToJavaBean(TypeUtils.java:1376) ... 2 more Caused by: java.time.format.DateTimeParseException: Text '09/11/2019 13:08:53' could not be parsed at index 0 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) at java.time.LocalDateTime.parse(LocalDateTime.java:492) at java.time.LocalDateTime.parse(LocalDateTime.java:477) at com.alibaba.fastjson.parser.deserializer.Jdk8DateCodec.parseDateTime(Jdk8DateCodec.java:285) at com.alibaba.fastjson.parser.deserializer.Jdk8DateCodec.deserialze(Jdk8DateCodec.java:93) at com.alibaba.fastjson.parser.deserializer.ContextObjectDeserializer.deserialze(ContextObjectDeserializer.java:9) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:682) ... 9 more

示例demo FastJsonDemo

回答

0

fastjson版本是1.2.61 lombok是1.16.18

issue下面有demo的哦

发自我的iPhone

请问使用的版本?

5

看了下demo,原因在于,整个反序列化的过程不是直接把json字符串转换为JavaBean对象,而是

  1. 先用JSON.parseObject()方法将json字符串转化为了JSONObject对象
  2. 再用JSONObject.toJavaObject()方法把JSONObject对象转换为了JavaBean对象。

而需要注意的一点是,步骤1中生成的JSONObject对象中只是简单地用了一个Map成员来存储原Json字符串中的键值对对应关系,不会保留注解信息。 所以在步骤2中,解析LocalDateTime的时候,由于没有注解中的元数据作指导,fastjson会用自己的一套逻辑来判断时间格式。而其中有两个格式非常接近:

"MM/dd/yyyy HH:mm:ss"  //美标
"dd/MM/yyyy HH:mm:ss" //欧标

这两种格式区别在于日和月的位置先后不同。当前的逻辑是通过判断数字是否大于12来猜测数字表示的是日还是月。如果两个数字都不大于12,则通过当前Locale中设置的国家信息来判断。Jdk8DateCodec.java中的相关代码如下:

int v0 = (c0 - '0') * 10 + (c1 - '0');
int v1 = (c3 - '0') * 10 + (c4 - '0');
if (v0 > 12) {
    formatter = formatter_dt19_eur;
} else if (v1 > 12) {
    formatter = formatter_dt19_us;
} else {
    String country = Locale.getDefault().getCountry();

    if (country.equals("US")) {
        formatter = formatter_dt19_us;
    } else if (country.equals("BR") //
            || country.equals("AU")) {
        formatter = formatter_dt19_eur;
    }
}

所以,在用户既没有配置Locale,日和月又都不大于12的情况下,日期格式会因为无法准确判断而取值为null,导致后续的解析步骤因为缺乏格式信息而抛出异常。(09/11会报错而09/13不会也是因为这个原因)

解决方式就是反序列化的时候直接一步到位,使用TestEntity testEntity = JSON.parseObject(jsonString, TestEntity.class)的方式。

9

看了下demo,原因在于,整个反序列化的过程不是直接把json字符串转换为JavaBean对象,而是

  1. 先用JSON.parseObject()方法将json字符串转化为了JSONObject对象
  2. 再用JSONObject.toJavaObject()方法把JSONObject对象转换为了JavaBean对象。

而需要注意的一点是,步骤1中生成的JSONObject对象中只是简单地用了一个Map成员来存储原Json字符串中的键值对对应关系,不会保留注解信息。 所以在步骤2中,解析LocalDateTime的时候,由于没有注解中的元数据作指导,fastjson会用自己的一套逻辑来判断时间格式。而其中有两个格式非常接近:

"MM/dd/yyyy HH:mm:ss"  //美标
"dd/MM/yyyy HH:mm:ss" //欧标

这两种格式区别在于日和月的位置先后不同。当前的逻辑是通过判断数字是否大于12来猜测数字表示的是日还是月。如果两个数字都不大于12,则通过当前Locale中设置的国家信息来判断。Jdk8DateCodec.java中的相关代码如下:

int v0 = (c0 - '0') * 10 + (c1 - '0');
int v1 = (c3 - '0') * 10 + (c4 - '0');
if (v0 > 12) {
    formatter = formatter_dt19_eur;
} else if (v1 > 12) {
    formatter = formatter_dt19_us;
} else {
    String country = Locale.getDefault().getCountry();

    if (country.equals("US")) {
        formatter = formatter_dt19_us;
    } else if (country.equals("BR") //
            || country.equals("AU")) {
        formatter = formatter_dt19_eur;
    }
}

所以,在用户既没有配置Locale,日和月又都不大于12的情况下,日期格式会因为无法准确判断而取值为null,导致后续的解析步骤因为缺乏格式信息而抛出异常。(09/11会报错而09/13不会也是因为这个原因)

解决方式就是反序列化的时候直接一步到位,使用TestEntity testEntity = JSON.parseObject(jsonString, TestEntity.class)的方式。

问题已解决,感谢解答

4

如果代码逻辑要求必须分2步解析json怎么办? 比如:调用spring-boot-data-rest提供的get服务时,返回数据除了list里的数据外,还包含有其他数据,例如: { "_embedded" : { "tasks" : [ { "name" : "1", "startTime" : "2019-12-16 08:28:49", "finishTime" : "2019-12-12 08:28:49", "state" : "created", "readme" : "readme", "_links" : { "self" : { "href" : "http://localhost:8001/api/task/1" }, "taskItem" : { "href" : "http://localhost:8001/api/task/1" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8001/api/task{?page,size,sort}", "templated" : true }, "profile" : { "href" : "http://localhost:8001/api/profile/task" } }, "page" : { "size" : 20, "totalElements" : 1, "totalPages" : 1, "number" : 0 } } 而实际使用上有时候只需要解析json中的tasks列表即可,此时,就必须先把json string转换为JSONObject,然后提取其中的tasks节点,然后再转换为具体的Task对象。