由fastjson对date类型处理引发
文章目录
案件重现
1. 结论:
JSONObject会把Date或TimeStamp类型的转成时间戳
2. 代码
**POJO**
public class RoleModel {
private Date lastUpdateTime;
//private Timestamp lastUpdateTime;
}
**mybatis**
<result column="last_update_time" property="lastUpdateTime" jdbcType="TIMESTAMP"/>
连接数据库--从数据库中打印(返回给前台)
public static void main(String[] args) throws Exception{
SqlSessionFactory sessionFactory = getSessionFactory();
SqlSession sqlSession = sessionFactory.openSession();
sqlSession.getConnection().setAutoCommit(true);
RoleModel roles = roleDao.getRoles(1);
//重点在这=====================================
System.out.println(roles.getLastUpdateTime());//lastUpdateTime是Date类型 Wed Nov 21 20:09:22 CST 2018 lastUpdateTime是Timestamp 类型 2018-11-21 20:09:22.0
System.out.println(JSONObject.toJSON(roles));//"lastUpdateTime":1542802162000
//=====================================
}
`
源码跟踪
DateCodec
public class DateCodec extends AbstractDateDeserializer implements ObjectSerializer, ObjectDeserializer {
//重点 没有指定UseISO8601DateFormat 就直接 out.writeLong(time);
//指定之后 calendar.setTimeInMillis(time); 利用IOUtils.getChars把calendar中的年月日赋值给buf数组,通过out输出
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
SerializeWriter out = serializer.out;
if (object == null) {
out.writeNull();
return;
}
Date date;
if (object instanceof Date) {
date = (Date) object;
} else {
date = TypeUtils.castToDate(object);
}
// 全局指定时间格式
if (out.isEnabled(SerializerFeature.WriteDateUseDateFormat)) {
DateFormat format = serializer.getDateFormat();
if (format == null) {
format = new SimpleDateFormat(JSON.DEFFAULT_DATE_FORMAT, serializer.locale);
format.setTimeZone(serializer.timeZone);
}
String text = format.format(date);
out.writeString(text);
return;
}
if (out.isEnabled(SerializerFeature.WriteClassName)) {
if (object.getClass() != fieldType) {
if (object.getClass() == java.util.Date.class) {
out.write("new Date(");
out.writeLong(((Date) object).getTime());
out.write(')');
} else {
out.write('{');
out.writeFieldName(JSON.DEFAULT_TYPE_KEY);
serializer.write(object.getClass().getName());
out.writeFieldValue(',', "val", ((Date) object).getTime());
out.write('}');
}
return;
}
}
long time = date.getTime();
if (out.isEnabled(SerializerFeature.UseISO8601DateFormat)) {
char quote = out.isEnabled(SerializerFeature.UseSingleQuotes) ? '\'' : '\"';
out.write(quote);
Calendar calendar = Calendar.getInstance(serializer.timeZone, serializer.locale);
calendar.setTimeInMillis(time);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
int millis = calendar.get(Calendar.MILLISECOND);
// IOUtils.getChars() 把calendar中的时间赋值给buf
char[] buf;
if (millis != 0) {
buf = "0000-00-00T00:00:00.000".toCharArray();
IOUtils.getChars(millis, 23, buf);
IOUtils.getChars(second, 19, buf);
IOUtils.getChars(minute, 16, buf);
IOUtils.getChars(hour, 13, buf);
IOUtils.getChars(day, 10, buf);
IOUtils.getChars(month, 7, buf);
IOUtils.getChars(year, 4, buf);
} else {
if (second == 0 && minute == 0 && hour == 0) {
buf = "0000-00-00".toCharArray();
IOUtils.getChars(day, 10, buf);
IOUtils.getChars(month, 7, buf);
IOUtils.getChars(year, 4, buf);
} else {
buf = "0000-00-00T00:00:00".toCharArray();
IOUtils.getChars(second, 19, buf);
IOUtils.getChars(minute, 16, buf);
IOUtils.getChars(hour, 13, buf);
IOUtils.getChars(day, 10, buf);
IOUtils.getChars(month, 7, buf);
IOUtils.getChars(year, 4, buf);
}
}
out.write(buf);
int timeZone = calendar.getTimeZone().getRawOffset()/(3600*1000);
if (timeZone == 0) {
out.write('Z');
} else {
if (timeZone > 0) {
out.append('+').append(String.format("%02d", timeZone));
} else {
out.append('-').append(String.format("%02d", -timeZone));
}
out.append(":00");
}
out.write(quote);
} else {
out.writeLong(time);
}
}
解决方案
- 指定UseISO8601DateFormat
//重点在这===================================================================
System.out.println(JSONObject.toJSONString(roles,SerializerFeature.UseISO8601DateFormat));//"lastUpdateTime":"2018-11-21T20:09:22+08:00" //但是这种又带T又带时区的格式(见上边的源码 )也不常用啊
//==========================================================================
/* 只需要指定全局时间格式 WriteDateUseDateFormat
*/
//重点在这===================================================================
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
JSON.toJSONString(roleDao.getRoles(1).getLastUpdateTime(), SerializerFeature.WriteDateUseDateFormat);//"2018-11-21 20:09:22"
System.out.println(JSON.toJSONString(roleDao.getRoles(1).getLastUpdateTime(), SerializerFeature.WriteDateUseDateFormat));
System.out.println(JSON.toJSONString(roleDao.getRoles(1).getLastUpdateTime(),SerializerFeature.WriteDateUseDateFormat));//也需要指定给特征。但是默认的时间格式就不用在设置了
//还可以------------------------------------------------------------------------------------------
String jsonStringWithDateFormat = JSON.toJSONStringWithDateFormat(object, "yyyy-MM-dd");
String jsonStringWithDateFormat = JSONObject.toJSONStringWithDateFormat(object, "yyyy-MM-dd");
//==========================================================================
- 前端转换toJsonString()返回的时间戳;//由于我的实体类是Date 或者时间戳类型的,通过Controller中的字段匹配时,前端也得传时间戳或者前端转成的Date() // 需核实
其他属性
源码 SerializerFeature
/**
* @author wenshao[[email protected]]
*/
public enum SerializerFeature {
QuoteFieldNames,
UseSingleQuotes,
WriteMapNullValue,
/**
* 用枚举toString()值输出
*/
WriteEnumUsingToString,
/**
* 用枚举name()输出
*/
WriteEnumUsingName,
UseISO8601DateFormat,
/**
* @since 1.1
*/
WriteNullListAsEmpty,
/**
* @since 1.1
*/
WriteNullStringAsEmpty,
/**
* @since 1.1
*/
WriteNullNumberAsZero,
/**
* @since 1.1
*/
WriteNullBooleanAsFalse,
/**
* @since 1.1
*/
SkipTransientField,
/**
* @since 1.1
*/
SortField,
/**
* @since 1.1.1
*/
@Deprecated
WriteTabAsSpecial,
/**
* @since 1.1.2
*/
PrettyFormat,
/**
* @since 1.1.2
*/
WriteClassName,
/**
* @since 1.1.6
*/
DisableCircularReferenceDetect, // 32768
/**
* @since 1.1.9
*/
WriteSlashAsSpecial,
/**
* @since 1.1.10
*/
BrowserCompatible,
/**
* @since 1.1.14
*/
WriteDateUseDateFormat,
/**
* @since 1.1.15
*/
NotWriteRootClassName,
/**
* @since 1.1.19
* @deprecated
*/
DisableCheckSpecialChar,
/**
* @since 1.1.35
*/
BeanToArray,
/**
* @since 1.1.37
*/
WriteNonStringKeyAsString,
/**
* @since 1.1.42
*/
NotWriteDefaultValue,
/**
* @since 1.2.6
*/
BrowserSecure,
/**
* @since 1.2.7
*/
IgnoreNonFieldGetter,
/**
* @since 1.2.9
*/
WriteNonStringValueAsString,
/**
* @since 1.2.11
*/
IgnoreErrorGetter,
/**
* @since 1.2.16
*/
WriteBigDecimalAsPlain,
/**
* @since 1.2.27
*/
MapSortField
;
SerializerFeature(){
mask = (1 << ordinal());
}
public final int mask;
public final int getMask() {
return mask;
}
public static boolean isEnabled(int features, SerializerFeature feature) {
return (features & feature.mask) != 0;
}
public static boolean isEnabled(int features, int fieaturesB, SerializerFeature feature) {
int mask = feature.mask;
return (features & mask) != 0 || (fieaturesB & mask) != 0;
}
public static int config(int features, SerializerFeature feature, boolean state) {
if (state) {
features |= feature.mask;
} else {
features &= ~feature.mask;
}
return features;
}
public static int of(SerializerFeature[] features) {
if (features == null) {
return 0;
}
int value = 0;
for (SerializerFeature feature: features) {
value |= feature.mask;
}
return value;
}
public final static SerializerFeature[] EMPTY = new SerializerFeature[0];
public static final int WRITE_MAP_NULL_FEATURES
= WriteMapNullValue.getMask()
| WriteNullBooleanAsFalse.getMask()
| WriteNullListAsEmpty.getMask()
| WriteNullNumberAsZero.getMask()
| WriteNullStringAsEmpty.getMask()
;
}
需要说明的
- fastJson 默认会对所有为null的对象值或者集合过滤掉,如果不想要过滤,需要设置
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteNullListAsEmpty
- 对于时间的处理
会把Date类型TimeStamp类型的以时间戳返回
json库对比
介绍
fastjson
目前最佳的是阿里的fastJson,fastjson是目前java语言中最快的json库,比自称最快的jackson速度要快.fastjson比gson快大约倍.https://github.com/alibaba/fastjson/wiki/常见问题.
Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。FastJson在复杂类型(我测试的类型没有问题)的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。☆☆☆☆☆
Gson
Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是JSON解析的神器。慢 ☆☆
Jackson
相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。而且Jackson社区相对比较活跃,更新速度也比较快。Jackson对于复杂类型(我测试的类型没有问题)的json转换bean会出现问题,一些集合Map,List的转换出现问题。Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式。☆☆☆☆
需要依赖:
//整个库(使用最新的2.2版本)包含3个jar包:
jackson-core.jar——核心包(必须),提供基于“流模式”解析的API。
jackson-databind——数据绑定包(可选),提供基于“对象绑定”和“树模型”相关API。
jackson-annotations——注解包(可选),提供注解功能。
Json-lib
json-lib最开始的也是应用最广泛的json解析工具,json-lib 不好的地方确实是依赖于很多第三方包,包括commons-beanutils.jar,commons-collections-3.2.jar,commons-lang-2.6.jar,commons-logging-1.1.1.jar,ezmorph-1.0.6.jar,对于复杂类型的转换,json-lib对于json转换成bean还有缺陷,比如一个类里面会出现另一个类的list或者map集合,json-lib从json到bean的转换就会出现问题。json-lib在功能和性能上面都不能满足现在互联网化的需求。☆
具体不同
- 对于null的值
com.alibaba.fastjson.JSON 只序列化已经赋值的属性;
jackson、org.json、net.sf.json、org.json.simple 所有属性都会序列化,要么为null 要么是0.0
com.google.gson. 不序列化为null的属性,但序列化没有赋值的int类型
- 对于时间类型
com.alibaba.fastjson. 处理比较方便。
Date create = object.setCreateTime(date);
- 设置SerializerFeature属性
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
String jsonStringWithDateFormat = JSON.toJSONString(object,SerializerFeature.WriteDateUseDateFormat);//也需要指定给特征。但是默认的时间格式就不用在设置了
- toJSONStringWithDateFormat
String jsonStringWithDateFormat = JSON.toJSONStringWithDateFormat(object, "yyyy-MM-dd");
String jsonStringWithDateFormat = JSONObject.toJSONStringWithDateFormat(object, "yyyy-MM-dd");
//抽象类都是静态方法
public abstract class JSON implements JSONStreamAware, JSONAware {}
//是JSON 的实现类
public class JSONObject extends JSON implements Map<String, Object>, Cloneable, Serializable, InvocationHandler {}
其他类 就不介绍了