为什么要换掉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. 【Matlab编程】matlab 画图

    1.  不用截图工具就可以将图保存成图像格式,并且没有背景颜色:saveas(gcf ,'outputname','png/jpg'),第三项省略时默认为fig.m文件 2.  计算形如(-1)^2/ ...

  2. Android Bootloader LittleKernel的两篇文章

    Android 开发之 ---- bootloader (LK) LK是什么 LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程 ...

  3. The content of elements must consist of well-formed character data or markup

    java 中使用dom4j解析含有特殊字符的xml文件出现了如题的错误 这个时候需要在特殊字符外面加上 <![CDATA[ /6169220648+20671/1>7+-47390045& ...

  4. linux下如何查询未知库所依赖的包

    经常会遇到linux下安装软件时提示少文件,如何知道所缺少的文件属于哪个包?用什么命令查看? 例如:/lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录 ...

  5. 面试之路(4)-TCP/IP/HTTP概述

    tcp/ip基础知识 TCP/IP全称是Transmission Control Protocol/Internet Protocol. IP地址共32位,4字节. IP地址分为两部分:网络标识和主机 ...

  6. Java核心技术第四章——1.封装性

    封装性(有时称为数据隐藏): 实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域值.程序仅通过对象的方法与对象的数据进行交互. 给对象赋予了"黑盒"的特征,提高了重用性 ...

  7. AMDP + XLSX Workbench 报表开发模式

    本文介绍了我和同事通过使用AMDP + XLSX Workbench缩短报表开发周期.分离数据查询处理逻辑和前端展示工作的经验.欢迎讨论. 前言 最近接到了一套人力资源报表的开发需求,需要以EXCEL ...

  8. 视频博文结合的教程:用nodejs实现简单的爬虫

    教学视频地址: https://v.qq.com/x/page/b0643tut4ze.html 前言   本喵最近工作中需要使用node,并也想晋升为全栈工程师,所以开始了node学习之旅,在学习过 ...

  9. 在SQL Server 2008 Management Studio中修改表字段顺序

    有时我们可能需要为一个已存在的数据库表添加字段,并且想让这个字段默认排的靠前一些,这时就需要为表字段重新进行排序,默认情况下在Management Studio中调整顺序并保存时会提示"不允 ...

  10. 转载一篇makefile,说的很详细

    March 3, 2015 8:19 PM 原文见:https://www.cnblogs.com/OpenShiFt/p/4313351.html Makefile 文件的编写 学习前的准备 需要准 ...