解决fastjson反序列化日期0000-00-00失败的方案

22 Jul 2016

一、案例场景复原

示例场景里涉及两个class:TestDemo.java, DateBeanDemo.java

// DateBeanDemo.java
public class DateBeanDemo {
/**
* dateStr field with Date.class
*/
private Date dateStr;
<span class="cm">/**
* Get dateStr &lt;br&gt;
*
* @return Returns the dateStr. &lt;br&gt;
*/</span>
<span class="kd">public</span> <span class="n">Date</span> <span class="nf">getDateStr</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">dateStr</span><span class="o">;</span>
<span class="o">}</span> <span class="cm">/**
* Set dateStr &lt;br&gt;
*
* @param dateStr The dateStr to set. &lt;br&gt;
*/</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setDateStr</span><span class="o">(</span><span class="n">Date</span> <span class="n">dateStr</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">dateStr</span> <span class="o">=</span> <span class="n">dateStr</span><span class="o">;</span>
<span class="o">}</span>

}

// 示例执行例子
public class TestDemo {
public static String jsonStr = "{\"dateStr\":\"0000-00-00\"}";
public static void main(String[] args) {
DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo.class);
}
}

执行以上的main方法之后,并没有获取预期的结果,而是在fastjson的序列化解析中便发生了异常,如下

Exception in thread "main" com.alibaba.fastjson.JSONException: For input string: "0000-00-00"
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:555)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:251)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:227)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:186)
at excel.TestDemo.main(TestDemo.java:23)
Caused by: java.lang.NumberFormatException: For input string: "0000-00-00"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Long.parseLong(Long.java:419)
at java.lang.Long.parseLong(Long.java:468)
at com.alibaba.fastjson.parser.deserializer.DateDeserializer.cast(DateDeserializer.java:56)
at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:98)
at Fastjson_ASM_DateBeanDemo_1.deserialze(Unknown Source)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:551)
... 4 more

通过百度查阅了部分网上撰写的方案,可以使用fastjson中的注解@JSONField(format="")来重新定义DateBeanDemo.dateStr,如下:

@JSONField(format = "yyyy-MM-dd",parseFeatures={Feature.AllowISO8601DateFormat})
private Date dateStr;

然而,并没有解决这个exception的问题。同时还怀疑了注解是否在反序列化之时没有被使用到,经过查阅源码,fastjson已将其反序列化的开关定义了true

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface JSONField {
int ordinal() default 0;
String name() default "";
String format() default "";
boolean serialize() default true;
boolean deserialize() default true; // 默认定义了true,反序列化之时也使用该注解
SerializerFeature[] serialzeFeatures() default {};
Feature[] parseFeatures() default {};
String label() default "";
}

二、原因剖析

经查找fastjson中相关代码发现,当代码执行到com.alibaba.fastjson.parser.deserializer.DateDeserializer.class的执行方法cast()中之时,所使用的JSONParser中所含带的dateFormat依旧是默认的yyyy-MM-dd HH:mm:ss,而并非注解@JSONField中所定义的yyyy-MM-dd
所以发生了转换字段失败。

再深究一层,为什么不是@JSONField中所定义的yyyy-MM-dd作为JSONParser中的dateFormat呢?其实仔细阅读一遍cast()代码逻辑就会发现,并不是fastjson丢弃了JSONField的扫描,而是在方法中有这么一段:

// 检查格式是否符合ISO8601的DateFormat规范
if(!dateLexer.scanISO8601DateIfMatch(false)) {
break label122;
}

当执行到以上代码段之时,由于字符串0000-00-00并不是ISO8601的DateFormat规范之内,故而代码便会break label122执行跳出逻辑。紧接着执行的就是DateFormat dateFormat1 = parser.getDateFormat();,此时,parser依然是global定义的parser,DateFormat并没有使用@JSONField中所定义的。

三、解决方案:新增date反序列化解析器

解决方案并非只有一种,在众多解决方案中自己选择了”新增date反序列化解析器”的办法。除此之外还有诸如设置JSON.DEFFAULT_DATE_FORMAT属性的办法,也同样可以解决这一问题。下面作两种办法的对比和阐述。

  • 方案1:设置属性JSON.DEFFAULT_DATE_FORMAT

这一方案只涉及修改main()方法代码即可实现,

// 示例执行例子
public class TestDemo {
public static String jsonStr = "{\"dateStr\":\"0000-00-00\"}";
public static void main(String[] args) {
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo.class);
}
}

但是翻阅fastjson中对于JSON.DEFFAULT_DATE_FORMAT可知,该属性属于静态属性,一旦设置影响全局。

// com.alibaba.fastjson.JSON.class
public abstract class JSON implements JSONStreamAware, JSONAware {
public static String DEFFAULT_DATE_FORMAT;
// ...
  • 方案2:新增date反序列化解析器

主要思路是以fastjson原生的DateDeserializer.class为基础,定制化一个可以解析0000-00-00的日期反序列化解析器。

该方式是fastjson函数JSON.parseObject()的一个应用场景,通过定制化ParserConfig参数,达到局部改变JSON解析逻辑的目的。

如下:

package jeromechan.fixbug.fastjson;

import com.alibaba.fastjson.parser.DefaultJSONParser;

import com.alibaba.fastjson.parser.deserializer.DateDeserializer;

import java.lang.reflect.Type; /**
  • Copyright © 2016 Jerome Chan. All rights reserved.
  • An extended DateDeseializer for parsing '0000-00-00'.
  • @author chenjinlong
  • @CreateDate 7/20/16 5:55 PM

    */

    public class JCDateDeserializer extends DateDeserializer {

    public static final JCDateDeserializer instance = new JCDateDeserializer(); public JCDateDeserializer() {

    } protected <T> T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Object val)

    {

    if (val null) {

    return null;

    } else if (val instanceof String) {

    String strVal = (String) val;

    if (strVal.length() 0) {

    return null;

    } else if (strVal.equals("0000-00-00")) {

    parser.setDateFormat("yyyy-MM-dd");

    }

    }

    return super.cast(parser, clazz, fieldName, val);

    }

    }

// 示例执行例子
public class TestDemo {
public static String jsonStr = "{\"dateStr\":\"0000-00-00\"}";
public static void main(String[] args) {
ParserConfig jcParserConfig = new ParserConfig();
jcParserConfig.putDeserializer(Date.class, JCDateDeserializer.instance);
DateBeanDemo resultObject = JSON.parseObject(TestDemo.jsonStr, DateBeanDemo.class, jcParserConfig, JSON.DEFAULT_PARSER_FEATURE);
}
}

假设觉得这种解析办法可以作为整个项目内的全局特性,感兴趣的话可以将定制好的JCDateDeserializer利用spring框架注入到项目容器中。这同样是对于方案2很不错的延伸。

四、参考资料

Related Posts

      <li>
    <a href="/2017/05/16/programming-principles/">
    Programming Principles(编程准则)
    <small>16 May 2017</small>
    </a>
    </li> <li>
    <a href="/2016/12/19/parallel-of-buying-scenario/">
    并发场景实践方案优劣评析
    <small>19 Dec 2016</small>
    </a>
    </li> <li>
    <a href="/2016/12/06/git-fetch-among-branch/">
    Git协作之合并代码
    <small>06 Dec 2016</small>
    </a>
    </li> <li>
    <a href="/2016/12/02/funny-function-invoking-logic/">
    几种方法调用思路的比较
    <small>02 Dec 2016</small>
    </a>
    </li> <li>
    <a href="/2016/09/23/feature-toggle-translation/">
    [译]Feature Toggle - Martin Fowler
    <small>23 Sep 2016</small>
    </a>
    </li> <li>
    <a href="/2016/09/06/db-master-slave-pattern-rules/">
    读写分离的原则
    <small>20 Sep 2016</small>
    </a>
    </li>

Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a>

解决fastjson反序列化日期0000-00-00失败的方案的更多相关文章

  1. 解决:"2013-01-06 00:00:00" is not a valid date and time.

    在转换时间格式时,遇到以下问题: 弹出对话框:"2013-01-06 00:00:00" is not a valid date and time. 在百度上查找,发现是本地日期格 ...

  2. 错误:Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp;的解决

    问题: 代码中查询MySQL的结果集时报错,提示Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp;刚开始 ...

  3. Mysql sql_mode设置 timestamp default 0000-00-00 00:00:00 创建表失败处理

    往数据库里创建新表的时候报错: [Err] 1067 - Invalid default value for 'updateTime' DROP TABLE IF EXISTS `passwd_res ...

  4. mysql解决Value ‘0000-00-00 00:00:00’ can not be represented as java.sql.Timestamp

    同步发布:http://www.yuanrengu.com/index.php/mysqlsolvetimestamp.html 在使用mysql时,如果数据库中的字段类型是timestamp,默认为 ...

  5. Mysql 时间格式默认空串 '0000-00-00 00:00:00' select抛出异常的解决方法

    Mysql 时间格式默认插入值为空时,会以'0000-00-00 00:00:00'填充,这时如果select时会抛出SQLExecption如下: java.sql.SQLException: Va ...

  6. Oracle数据库使用mybatis的时候,实体类日期为Date类型,mybatis里面定义的是Date类型,插入的时候,时分秒全部是12:00:00问题

    实体类中日期定义的是Date类型的,没毛病: 我在mybatis里面定义的是Date类型的,进行测试的时候发现,数据库插入的日期的时分秒全部都是一样的,都是12:00:00,很郁闷: 后来把mybat ...

  7. 解决:SqlDateTime 溢出。必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间提示问题

    提示信息如下 “/”应用程序中的服务器错误. SqlDateTime 溢出.必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间. 问题现象: 问 ...

  8. 日期处理相关 - “Fri Dec 11 00:00:00 CST 2015”日期格式解析

    1.后台处理方式: /* 精简版解析 - 推荐 */ String a= "Fri Dec 11 00:00:00 CST 2015"; Date d = new Date(a); ...

  9. 解决:mysql5.7 timestamp默认值0000-00-00 00:00:00 报错

    解决:mysql5.7 timestamp默认值0000-00-00 00:00:00 报错 学习了:https://www.cnblogs.com/cnhkzyy/p/9119339.html se ...

随机推荐

  1. 五种网络IO模型以及多路复用IO中select/epoll对比

    下面都是以网络读数据为例 [2阶段网络IO] 第一阶段:等待数据 wait for data 第二阶段:从内核复制数据到用户 copy data from kernel to user 下面是5种网络 ...

  2. Linux shell cut 命令详解

    详细资料请参考:博客园骏马金龙 https://www.cnblogs.com/f-ck-need-u/p/7521357.html cut命令将行按指定的分隔符分割成多列,它的弱点在于不好处理多个分 ...

  3. web学习---html,js,php,mysql一个动态网页获取流程

    使用bootstrap的cms模版系统搭建了一个信息管理系统.通过这个系统学习动态网页获取的工作流程. 抓包分析一个页面的数据请求流程如下图所示: 同样,对于需要向数据库插入数据,可以使用ajax接口 ...

  4. jquer_shijian 增加初始化 年月日 及 结束时间 年月日

    增加了插件 在初始化的时候,控制 年月日,和结束 年月日 $(time_createobj).siblings(".xtw_budget_userdatafn_hide").shi ...

  5. wix中ServiceInstall与ServiceControl的关系

    上面那篇之后其实还踩了个坑,安装Windows服务确实是打包进去了,但死活不能安装成功,从提示和日志看正好是Windows服务处理的地方出现了异常.以为是服务启动失败,但是服务在服务管理里手动启动是没 ...

  6. 前端开发CSS清除浮动的方法有哪些?

    在前端开发过程中,非IE浏览器下,当容器的高度自动,并且容器内容中有浮动元素(float为left或right),此时如果容器的高度不能自适应内容的高度,从而使得内容溢出破坏整体布局,这种现象叫做浮动 ...

  7. Plastic Bottle Manufacturer Tips: Use Caution For Plastic Bottles

    Plastic bottles use polyester (PET), polyethylene (PE), polypropylene (PP) as raw materials, after a ...

  8. UVa 400 Unix Is命令

    简单题 #include <bits/stdc++.h> using namespace std; const int maxn=110; string s[maxn]; int main ...

  9. 什么是DO / DTO / BO / VO /AO ?

    转载:https://blog.csdn.net/ouzhuangzhuang/article/details/86644476 POJO 是 DO / DTO / BO / VO 的统称. DO(D ...

  10. 【MySQL】外键的变种

    " 目录 三种关系 多对一 多对多 一对一 因为有foreign key的约束,使得两张表形成了三种关系: 多对一 多对多 一对多 重点理解如何找出两张表之间的关系 现在有A.B两张表 分析 ...