为什么要换掉fastjson

直接原因是fastjson无法支持注解形式的自定义序列化和反序列化,虽然其Github上的Wiki上说明是支持的。但是实测结果表明:Test类的序列化被fastjson的ASMFactory生成字节码形式的序列化类代理,序列化的逻辑依然为原生而不是自定义的XXX.class

class Test{
@JSONField(usingSerializer=XXX.class,usingDeserializer=YYY.class)
private List<A> as;
//省略
}

在官方Wiki上没有找到关闭ASM特性的方法,自己尝试在构造FastJsonHttpMessageConverter时用Feature.DisableASM也没有效果。

<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4" id="fastJsonHttpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="features">
<list>
<value>DisableASM</value>
</list>
</property>
</property>
</bean>

总的来说,当使用JSON类库一些高级特性的时候,fastjson的表现没有jackson稳定,文档支持较之也不完善。如果项目不在意Json序列化/反序列化这块的性能,换用jackson是一种合理的选择。

关于技术选型的一点反思

根据网络上公开的资料比较得到下表。看起来不如不使用需要额外配置的高级特性,fastjson有官方的中文支持,学习难度也很低。

  fastjson jackson
上手容易度 容易 中等
高级特性支持 中等 丰富
官方文档、Example支持 中文、少、无体系 英文、较多
非官方资料 高级特性用例少、stackoverflow上问题少 中文资料较多

但是实际使用下来,fastjson的高级特性支持不完善、存在bug、得不到英文社区的支持,导致自己在业务实际应用时反而需要较多的时间去调试定位类库本身的问题。因此技术选型的时候要注意不能贪图容易上手,最好考虑其社区活跃度和网络上各种渠道的资料丰富程度,因为这在一定程度上展示了类库的健壮性。

如何兼容fastjson的一些特性

方便的序列化特性

fastjson提供了一些方便的序列化特性,下面设置的serializerFeatures特性主要针是对null的默认值处理。

<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4" id="fastJsonHttpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="serializerFeatures">
<list>
<value>WriteMapNullValue</value> <!--输出Map中的null值 -->
<value>WriteNullListAsEmpty</value> <!-- 引用为null的列表/数组/集合输出为[] -->
<value>WriteNullStringAsEmpty</value> <!-- 引用为null的String输出为“” -->
<value>WriteNullNumberAsZero</value> <!-- 引用为null的数字类型输出为0 -->
<value>WriteNullBooleanAsFalse</value> <!-- 引用为null的Boolean输出为false -->
</list>
</property>
</bean>
</property>
</bean>

遗憾的是,jackson并没有提供如此方便的设置,jackson的核心类ObjectMapper暴露的NullSerializer无法针对不同类型设定不同的默认值,所幸jackson有完善的扩展机制。BeanSerializerModifierchangeProperties()方法提供了细粒度控制每个Field的序列化行为的可能,代码如下。

public enum SerializerFeature {
WriteNullListAsEmpty,
WriteNullStringAsEmpty,
WriteNullNumberAsZero,
WriteNullBooleanAsFalse; public final int mask; SerializerFeature() {
mask = (1 << ordinal());
}
} final public static class FastJsonSerializerFeatureCompatibleForJackson extends BeanSerializerModifier {
final private JsonSerializer<Object> nullBooleanJsonSerializer;
final private JsonSerializer<Object> nullNumberJsonSerializer;
final private JsonSerializer<Object> nullListJsonSerializer;
final private JsonSerializer<Object> nullStringJsonSerializer; FastJsonSerializerFeatureCompatibleForJackson(SerializerFeature... features) {
int config = 0;
for (SerializerFeature feature : features) {
config |= feature.mask;
}
nullBooleanJsonSerializer = (config & WriteNullBooleanAsFalse.mask) != 0 ? new NullBooleanSerializer() : null;
nullNumberJsonSerializer = (config & WriteNullNumberAsZero.mask) != 0 ? new NullNumberSerializer() : null;
nullListJsonSerializer = (config & WriteNullListAsEmpty.mask) != 0 ? new NullListJsonSerializer() : null;
nullStringJsonSerializer = (config & WriteNullStringAsEmpty.mask) != 0 ? new NullStringSerializer() : null;
} @Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
final JavaType javaType = writer.getType();
final Class<?> rawClass = javaType.getRawClass();
if (javaType.isArrayType() || javaType.isCollectionLikeType()) {
writer.assignNullSerializer(nullListJsonSerializer);
} else if (Number.class.isAssignableFrom(rawClass) && rawClass.getName().startsWith("java.lang")) {
writer.assignNullSerializer(nullNumberJsonSerializer);
} else if (Boolean.class.equals(rawClass)) {
writer.assignNullSerializer(nullBooleanJsonSerializer);
} else if (String.class.equals(rawClass)) {
writer.assignNullSerializer(nullStringJsonSerializer);
}
}
return beanProperties;
} private static class NullListJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartArray();
jgen.writeEndArray();
}
} private static class NullNumberSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeNumber(0);
}
} private static class NullBooleanSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeBoolean(false);
}
} private static class NullStringSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString("");
}
}
}

最后把该Modifier注入到ObjectMapper中。

objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(newFastJsonSerializerFeatureCompatibleForJackson(features)));

反序列化时忽略不存在的字段

在标准Json规范中,如果尝试反序列化一个Json字符串为指定的POJO,而且字符串中有一个Field在POJO中不存在,应该抛出错误。对于这种情况,fastjson默认是跳过,jackson默认是中止。

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

重新设置SpringMVC的HttpMessageConvertor

<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.dianping.orderdish.framework.util.CustomJacksonGenerator.CustomObjectMapperFactoryBean">
<constructor-arg>
<util:list>
<value>WriteNullListAsEmpty</value>
<value>WriteNullStringAsEmpty</value>
<value>WriteNullNumberAsZero</value>
<value>WriteNullBooleanAsFalse</value>
</util:list>
</constructor-arg>
</bean>
</property>
</bean>

由于需要编程式的设置全局配置,扩展FactoryBeanCustomObjectMapperFactoryBean)生成自定义的ObjectMapper来替代MappingJackson2HttpMessageConverter中默认ObjectMapper

final public static class CustomObjectMapperFactoryBean implements FactoryBean<ObjectMapper> {
SerializerFeature[] features;
public CustomObjectMapperFactoryBean(SerializerFeature[] features) {
this.features = features;
} @Override
public ObjectMapper getObject() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new FastJsonSerializerFeatureCompatibleForJackson(features))).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper;
} @Override
public Class<?> getObjectType() {
return ObjectMapper.class;
} @Override
public boolean isSingleton() {
return false;
}
}

Reference

  1. 【私人定制jackson】定制jackson的自定义序列化(null值的处理)
  2. Jackson Documentation

【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思的更多相关文章

  1. springmvc整合fastjson

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. 【SSM之旅】Spring+SpringMVC+MyBatis+Bootstrap整合基础篇(一)项目简介及技术选型相关介绍

    试水 一直想去搭建个自己的个人博客,苦于自己的技术有限,然后也个人也比较懒散.想动而不能动,想动而懒得动,就这么一直拖到了现在.总觉得应该把这几年来的所学总结一番,这样才能有所成长. 不知在何时,那就 ...

  3. Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简述及技术选型介绍

    作者:13GitHub:https://github.com/ZHENFENG13版权声明:本文为原创文章,未经允许不得转载. 萌芽阶段 很久之前就开始打算整理一下自己的技术博客了,由于各种原因(借口 ...

  4. maven+springmvc+easyui+fastjson+pagehelper

    1.maven配置 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...

  5. SpringMVC(十三):SpringMVC 与fastjson集成

    1)fastjson jar包下载地址:https://sourceforge.net/projects/fastjson/下载完成后需要把jar包拷贝到WEB-INF/lib文件夹中.2)使用pom ...

  6. springmvc与fastjson的整合,注解@RequestBody的使用

    项目内容用的是jetty框架,传输数据格式是json格式,有一天我心血来潮,把项目又搭建了一次,完了,卡在了数据传输的格式上,明明原来框架直接用fastjson,但是我用就是不对,总是报fastjso ...

  7. SpringMVC整合fastjson、easyui 乱码问题

    一.框架版本 SpringMVC:3.1.1.RELEASE fastjson:1.2.7 easyui :1.4.5 二.乱码现象    Action中使用@ResponseBody返回Json数据 ...

  8. SpringMVC使用fastjson自定义Converter支持返回jsonp格式(转)

    import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import c ...

  9. 描述下fastJSON,jackson等等的技术

    ①Jackson:依赖的jar包较少,简单易用性能高,更新速度也比较快,但是对于复杂类型的json转换bean会出 现问题,一些集合Map,List的转换出现问题,对于复杂类型的bean转换Json, ...

随机推荐

  1. LeetCode之“动态规划”:House Robber && House Robber II

    House Robber题目链接 House Robber II题目链接 1. House Robber 题目要求: You are a professional robber planning to ...

  2. Android绘图机制(三)——自定义View的实现方式以及半弧圆新控件

    Android绘图机制(三)--自定义View的三种实现方式以及实战项目操作 在Android绘图机制(一)--自定义View的基础属性和方法 里说过,实现自定义View有三种方式,分别是 1.对现有 ...

  3. 回顾一下shell脚本1

    首先: 1.编写一个hello.sh的程序 hello.sh vim hello.sh #!/bin/sh #作者:yangyx echo "hello shell"</sp ...

  4. WebService学习--(四)调用第三方提供的webService服务

    互联网上面有很多的免费webService服务,我们可以调用这些免费的WebService服务,将一些其他网站的内容信息集成到我们的Web应用中显示,下面就以获取天气预报数据和查询国内手机号码归属地为 ...

  5. xml与object 之间的ORM

    xml映射为object对象,同时object对象,以xml来表示: public class Tools { private static XmlNodeList SelectNodes(strin ...

  6. JavaScript 跨域之 POST 实现。

    javascript 跨域是一个很常见的问题,其中 jsonp 是一个最常用的手段,但是 jsonp 只支持 get,不支持 post,所以如果想通过 jsonp 来 post 一些数据,就头大了. ...

  7. 没人看系列----css 随笔

    目录 没人看系列----css 随笔 没人看系列----html随笔 前言 没什么要说的就是自己总结,学习用的如果想学点什么东西,请绕行. CSS (Cascading Style Sheets)层叠 ...

  8. mac os x 10.9.3 升级到10.10.4 记录

    昨天终于忍不住,将mac pro 的操作系统从 os x 10.9.3 升级到10.10.4,因为网络不给力,500k/s,光系统包都要5.6G,所以整整下来了一个工作白天,等下班的时候开始安装,不过 ...

  9. 爬取廖雪峰的python3教程

    从廖雪峰老师的python教程入门的,最近在看python爬虫,入手了一下 代码比较low,没有用到多线程和ip代理池 然后呢,由于robots.txt的限定,构建了一些user-agent,并放慢的 ...

  10. vue实现淘宝商品详情页属性选择功能

    方法一是自己想出来的,方法二来自忘记哪里看到的了 不知道是不是你要的效果: 方法一:利用input[type="radio"] css代码: input { display: no ...