说明:本文中的json-lib版本为

<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>

json-lib提供了将java对象与json字符串相互转换的能力,主要覆盖所有java基本类型与java基本类型的包装型。

相关代码如下:

net.sf.json.util.JSONUtils

   private static final MorpherRegistry morpherRegistry = new MorpherRegistry();

   static{
// register standard morphers
MorphUtils.registerStandardMorphers( morpherRegistry );
}

net.sf.ezmorph.MorphUtils

   /**
* Clears and registers all standard morpehrs.
*
* @param morpherRegistry
*/
public static void registerStandardMorphers( MorpherRegistry morpherRegistry )
{
morpherRegistry.clear();
registerStandardPrimitiveMorphers( morpherRegistry );
registerStandardPrimitiveArrayMorphers( morpherRegistry );
registerStandardObjectMorphers( morpherRegistry );
registerStandardObjectArrayMorphers( morpherRegistry );
}
   public static void registerStandardPrimitiveMorphers( MorpherRegistry morpherRegistry )
{
morpherRegistry.registerMorpher( new BooleanMorpher( false ) );
morpherRegistry.registerMorpher( new CharMorpher( '\0' ) );
morpherRegistry.registerMorpher( new ByteMorpher( (byte) 0 ) );
morpherRegistry.registerMorpher( new ShortMorpher( (short) 0 ) );
morpherRegistry.registerMorpher( new IntMorpher( 0 ) );
morpherRegistry.registerMorpher( new LongMorpher( 0 ) );
morpherRegistry.registerMorpher( new FloatMorpher( 0 ) );
morpherRegistry.registerMorpher( new DoubleMorpher( 0 ) );
}
   public static void registerStandardObjectMorphers( MorpherRegistry morpherRegistry )
{
morpherRegistry.registerMorpher( new BooleanObjectMorpher( Boolean.FALSE ) );
morpherRegistry.registerMorpher( new CharacterObjectMorpher( new Character( '\0' ) ) );
morpherRegistry.registerMorpher( StringMorpher.getInstance() );
morpherRegistry.registerMorpher( new NumberMorpher( Byte.class, new Byte( (byte) 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( Short.class, new Short( (short) 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( Integer.class, new Integer( 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( Long.class, new Long( 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( Float.class, new Float( 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( Double.class, new Double( 0 ) ) );
morpherRegistry.registerMorpher( new NumberMorpher( BigInteger.class, BigInteger.ZERO ) );
morpherRegistry.registerMorpher( new NumberMorpher( BigDecimal.class,
MorphUtils.BIGDECIMAL_ZERO ) );
morpherRegistry.registerMorpher( ClassMorpher.getInstance() );
}

主要涉及的类型都是在这里注册的,大家可以看到没有我们常见的日期型,json-lib本身并没有提供对日期型的支持,对于它来说日期型只是一般Object,在处理的时候都是当成一般对象来处理的,比如在java转json的时候,会生成如下的json

java对象:

import java.util.Date;

public class JSONTestEntity {
private Date aa;
private Timestamp bb;
private java.sql.Date cc;

转换的json

{"aa":{"date":9,"day":4,"hours":14,"minutes":56,"month":10,"seconds":44,"time":1510210604836,"timezoneOffset":-480,"year":117},
"bb":{"date":9,"day":4,"hours":14,"minutes":56,"month":10,"nanos":836000000,"seconds":44,"time":1510210604836,"timezoneOffset":-480,"year":117},"cc":null,"ss":"{:\"'},"}

可以看到aa被当成了一个对象来解析了,date的属性被反射了出来。同理timeStamp也是可以转化的。

util.Date还算是凑巧能解析出来,而其sql.Date就没这么幸运了。

sql.Date在转换的时候会报如下错误:

Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:2155)
at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1323)
at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:762)
at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:837)
at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:426)
at net.sf.json.JSONObject.defaultBeanProcessing(JSONObject.java:749)
... 13 more
Caused by: java.lang.IllegalArgumentException
at java.sql.Date.getHours(Date.java:143)

原因是sql.Date重写了util.Date的getHours方法

package java.sql;

public class Date extends java.util.Date {

   /**
* This method is deprecated and should not be used because SQL Date
* values do not have a time component.
*
* @deprecated
* @exception java.lang.IllegalArgumentException if this method is invoked
* @see #setHours
*/
public int getHours() {
throw new java.lang.IllegalArgumentException();
}

导致json-lib在执行反射的时候直接抛了这个异常。

所以这里我们也能看出在java->json的时候主要是用反射去取属性值,再执行相应的get方法来获得。

上面是java->json时候的问题,那么接下来,json->java能不能顺利进行呢?

我们将上面的生成的字符串回解析成java,结果发现报了如下错误:

Exception in thread "main" net.sf.json.JSONException: java.lang.NoSuchMethodException: java.sql.Timestamp.<init>()
at net.sf.json.JSONObject.toBean(JSONObject.java:288)
at net.sf.json.JSONObject.toBean(JSONObject.java:406)
at net.sf.json.JSONObject.toBean(JSONObject.java:233)
at com.aisino.wsbs.utils.JsonUtils.stringToJavaBean(JsonUtils.java:46)
at test.aisino.wsbs.utils.TestJsonValue.main(TestJsonValue.java:30)
Caused by: java.lang.NoSuchMethodException: java.sql.Timestamp.<init>()
at java.lang.Class.getConstructor0(Class.java:2706)
at java.lang.Class.getDeclaredConstructor(Class.java:1985)
at net.sf.json.util.NewBeanInstanceStrategy$DefaultNewBeanInstanceStrategy.newInstance(NewBeanInstanceStrategy.java:55)
at net.sf.json.JSONObject.toBean(JSONObject.java:282)
... 4 more

为什么呢?因为json-lib在将json转化成java对象的时候,需要实例化对象,它实例化对象是调用newInstance。而源码里它只会去取java对象的无参构造,毕竟它不知道你的java对象定义了几个构造函数,每个构造函数又传几个参数。

   private static final class DefaultNewBeanInstanceStrategy extends NewBeanInstanceStrategy {
private static final Object[] EMPTY_ARGS = new Object[0];
private static final Class[] EMPTY_PARAM_TYPES = new Class[0]; public Object newInstance( Class target, JSONObject source ) throws InstantiationException,
IllegalAccessException, SecurityException, NoSuchMethodException,
InvocationTargetException {
if( target != null ){
Constructor c = target.getDeclaredConstructor( EMPTY_PARAM_TYPES );
c.setAccessible( true );
try {
return c.newInstance( EMPTY_ARGS );
} catch ( InstantiationException e ) {
// getCause() was added on jdk 1.4

所以小伙伴们注意了:在用json-lib转java对象的时候,至少要有一个无参构造函数,或者不写任何构造函数。

而我们的TimeStamp恰巧不巧,没有!!!!

所以,毫无疑问的又报错了。

同样sql.Date也没有无参构造

如果去掉这个timestamp和sql.Date,只保留util.Date,那么会发现可以成功

好了,看上去只有util.Date可以正常的走完整个流程,但是这里还有一个坑:在json转java的过程中,如果对应的属性没有值会发生什么呢。

我们构造一个测试的string:

static final String testJsonStr = "{\"aa\":\"\",\"bb\":\"\",\"cc\":\"\",\"ss\":\"{:\\\"'},\"}";

JSONTestEntity jsonObject2 = JsonUtils.stringToJavaBean(testJsonStr, JSONTestEntity.class);
System.out.println(jsonObject2);

得到的结果如下:

JSONTestEntity [aa=Thu Nov 09 15:22:31 CST 2017, bb=, cc=null, ss={:"'},]

我们可以看到aa里面被赋上的当前时间,原因很简单,因为在初始化java对象Date的时候,newInstance等于是new Date(),它就是系统当前时间,而又没有任何值进行反射,所以就是当前时间。

注意,我们这里构造的json给aa的值是“”,如果给的null,则不会初始化

static final String testJsonStr = "{\"aa\":null,\"cc\":null,\"ss\":\"{:\\\"'},\"}";

这样的话,返回的结果是:

JSONTestEntity [aa=null, bb=, cc=null, ss={:"'},]

--------------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------------


好了,以上都是json-lib对java对象解析过程的分析。但是这只是知道一些现象的原因,并不是我想要的,我想要Date转成固定的日期格式,并且在为空的时候,反向序列化的时候不会有什么赋系统当前变量。怎么办!!!好办,我们只需要在java->json和json->java两个步骤上都插一脚即可。

java->json:

1,自定义JsonValueProcessor,实现其接口,具体实现网上有人写过了,我也是拷别人的,就不在此贴出来了,只贴个json-lib的接口定义。

package net.sf.json.processors;

import net.sf.json.JSONException;
import net.sf.json.JsonConfig; /**
* Base interface for custom serialization per property.
*
* @author Andres Almiray <aalmiray@users.sourceforge.net>
*/
public interface JsonValueProcessor {
/**
* Processes the value an returns a suitable JSON value.
*
* @param value the input value
* @return a valid JSON value that represents the input value
* @throws JSONException if an error occurs during transformation
*/
Object processArrayValue( Object value, JsonConfig jsonConfig ); /**
* Processes the value an returns a suitable JSON value.
*
* @param key the name of the property
* @param value the value of the property
* @return a valid JSON value that represents the input property
* @throws JSONException if an error occurs during transformation
*/
Object processObjectValue( String key, Object value, JsonConfig jsonConfig );
}

2,注册解析器

    public static String javaBeanToString(Object obj){
JsonConfig cfg = new JsonConfig();
cfg.registerJsonValueProcessor(java.sql.Timestamp.class, new DateJsonValueProcessor(null));
cfg.registerJsonValueProcessor(java.util.Date.class, new DateJsonValueProcessor(null));
cfg.registerJsonValueProcessor(java.sql.Date.class, new DateJsonValueProcessor(null));
JSONObject jsonObject = JSONObject.fromObject(obj, cfg);
return jsonObject.toString();
}

ok现在再执行java转json,会得到如下结果(PS:日期格式是我自己在DateJsonValueProcessor临时定义的):

{"aa":"2017-11-09 15:38:02","bb":"2017-11-09 15:38:02","cc":"2017-11-09 15:38:02","ss":"{:\"'},"}

json->java

1,自定义ObjectMorpher

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import org.springframework.util.StringUtils; import net.sf.ezmorph.ObjectMorpher; public class UtilDateMorpher implements ObjectMorpher {
private String format = "yyyy-MM-dd HH:mm:ss"; /**
* json转换成java object
* @param value json字符串
*
*/
@Override
public Object morph(Object value) {
SimpleDateFormat sf = new SimpleDateFormat(format);
if(StringUtils.isEmpty(value)){
return null;
}
try {
return sf.parse((String)value);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
} /**
* 对哪种java对象进行解析
*/
@Override
public Class morphsTo() {
return Date.class;
} /**
* 支持那种clazz类型的解析
*/
@Override
public boolean supports(Class clazz) {
if(clazz == String.class){
return true;
}
return false;
} }

这里我只是简单实现了一下,要注意supports方法,因为我们上一步已经把Date对象转换成字符串,所以这里传入的类型其实是string的

2,将自定义的Morpher注册到json-lib里

    /**
* 自定义增加如下三种日期型的解析
*/
static{
JSONUtils.getMorpherRegistry().registerMorpher(new UtilDateMorpher(), true);
JSONUtils.getMorpherRegistry().registerMorpher(new SqlDateMorpher(), true);
JSONUtils.getMorpherRegistry().registerMorpher(new TimeStampMorpher(), true);
}

由于json-lib的注册是写在静态代码块里的,所以这里我们只需要将这个写到我们调用json-lib工具类的静态代码块里即可。其他两种日期型的定义类似。

好了,现在再执行结果,可以看到转化正常了。包括如果入参是“”的时候也给返回null

JSONTestEntity [aa=Thu Nov 09 15:52:53 CST 2017, bb=2017-11-09 15:52:53.0, cc=2017-11-09, ss={:"'},]

特别注意:要统一自定义日期格式,序列化和反序列化的日期格式要对应起来。

以上就是两步转化中我们加入自定义实现的过程,至于详细的原理,包括json-lib转化的内部逻辑就不细说了,翻翻源代码就能看懂了

关于json-lib中日期类型转换的分析与问题解决的更多相关文章

  1. PostgreSQL 中日期类型转换与变量使用及相关问题

    PostgreSQL中日期类型与字符串类型的转换方法 示例如下: postgres=# select current_date; date ------------ 2015-08-31 (1 row ...

  2. Extjs4处理后台json数据中日期和时间的方法

    当ASP.NET后台使用JavaScriptSerializer这个组件将对象序列化为json,或者使用ScriptMethod特性的json [ScriptMethod(ResponseFormat ...

  3. 处理Json数据中的日期类型.如/Date(1415169703000)/格式

    在asp.net mvc后台返回到视图中的json数据中想对数据进行操作,发现日期类型无法直接进行操作,需要转换为指定格式才行.在网上也搜了下方法也很多,觉得有点麻烦,最终使用正则搞定了,分享下: v ...

  4. SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换

    SpringMVC表单或Json中日期字符串与JavaBean的Date类型的转换 场景一:表单中的日期字符串和JavaBean的Date类型的转换 在使用SpringMVC的时候,经常会遇到表单中的 ...

  5. js 中json遍历 添加 修改 类型转换

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  6. [转]SpringMVC日期类型转换问题三大处理方法归纳

    http://blog.csdn.net/chenleixing/article/details/45190371 前言 我们在SpringMVC开发中,可能遇到比较多的问题就是前台与后台实体类之间日 ...

  7. 使用JsonConfig控制JSON lib序列化

    将对象转换成字符串,是非常常用的功能,尤其在WEB应用中,使用 JSON lib 能够便捷地完成这项工作.JSON lib能够将Java对象转成json格式的字符串,也可以将Java对象转换成xml格 ...

  8. SpringMVC日期类型转换问题处理方法归纳

    前言 我们在SpringMVC开发中,可能遇到比较多的问题就是前台与后 台实体类之间日期转换处理的问题了,说问题也不大,但很多人开发中经常会遇到这个问题,有时很令人头疼,有时间问题暴露的不是很明显,然 ...

  9. 利用FastJson,拼接复杂嵌套json数据&&直接从json字符串中(不依赖实体类)解析出键值对

    1.拼接复杂嵌套json FastJson工具包中有两主要的类: JSONObject和JSONArray ,前者表示json对象,后者表示json数组.他们两者都能添加Object类型的对象,但是J ...

随机推荐

  1. 洗礼灵魂,修炼python(6)--活起来的代码+列表

    活起来的用法: 使用input内置函数 注意python2中和python3中,input函数是不太一样的,python2中,input用户传入什么类型就是什么类型而python3中,不管传入什么类型 ...

  2. hdu 2609 How many 最小表示法

    How many Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  3. FPGA与Deep Learning

    你还没听过FPGA?那你一定是好久没有更新自己在IT领域的知识了. FPGA全称现场可编程门阵列(Field-Programmable Gate Array),最初作为专用集成电路领域中的一种半定制电 ...

  4. M-移动端的webapp页面布局教程和webapp实战分析

    http://www.25xt.com/html5css3/8092.html 响应式设计 1 媒体查询 适用于不同固定宽度设计 媒体类型 : screen 屏幕 print 打印机 handheld ...

  5. 【学习】如何制作手机端html模板(REM的实际应用)

    以前制作手机页面时,总是很迷茫,不知从何着手,页面也不知如何处理.会用一些百分比啊,媒体查询啊,还有就是目测了,但是各种手机端的屏幕适配是个老大难的问题,没有做到百分百兼容的.自从发现了rem这个好东 ...

  6. 简易js模板引擎

    前面 js 模板引擎有很多很多,我以前经常用 art-template ,有时候也会拿 vue 来当模板引擎用. 直到...... 年初的时候,我还在上个项目组,那时候代码规范是未经允许不能使用 [外 ...

  7. win10 uwp 入门

    UWP是什么我在这里就不说,本文主要是介绍如何入门UWP,也是合并我写的博客. 关于UWP介绍可以参见:http://lib.csdn.net/article/csharp/32451 首先需要申请一 ...

  8. dotweb框架之旅 [四] - 常用对象-HttpContext

    dotweb属于一个Web框架,希望通过框架行为,帮助开发人员快速构建Web应用,提升开发效率,减少不必要的代码臃肿. dotweb包含以下几个常用对象: App(dotweb) App容器,为Web ...

  9. 上海2017QCon个人分享总结

    有幸作为讲师受邀参加InfoQ在上海举办的QCon2017,不得不说,不论是从讲师还是听众的角度衡量,QCon进一步扩大了技术视野.虽然前端专题只有四场,但每一场分享都是目前的热门话题.并且Qcon的 ...

  10. iOS开发中使用文字图标iconfont

    在iOS的开发中,各种图标的使用是不可避免的,如果把全部图标做成图片放在项目中,那么随着项目的逐渐庞大起来,图片所占的地方就会越来越大,安装包也就随之变大了,如果图标需要根据不同的场景改变使用不同的颜 ...