【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思
为什么要换掉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有完善的扩展机制。BeanSerializerModifier的changeProperties()方法提供了细粒度控制每个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>
由于需要编程式的设置全局配置,扩展FactoryBean(CustomObjectMapperFactoryBean)生成自定义的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
【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思的更多相关文章
- springmvc整合fastjson
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- 【SSM之旅】Spring+SpringMVC+MyBatis+Bootstrap整合基础篇(一)项目简介及技术选型相关介绍
试水 一直想去搭建个自己的个人博客,苦于自己的技术有限,然后也个人也比较懒散.想动而不能动,想动而懒得动,就这么一直拖到了现在.总觉得应该把这几年来的所学总结一番,这样才能有所成长. 不知在何时,那就 ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(一)项目简述及技术选型介绍
作者:13GitHub:https://github.com/ZHENFENG13版权声明:本文为原创文章,未经允许不得转载. 萌芽阶段 很久之前就开始打算整理一下自己的技术博客了,由于各种原因(借口 ...
- maven+springmvc+easyui+fastjson+pagehelper
1.maven配置 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...
- SpringMVC(十三):SpringMVC 与fastjson集成
1)fastjson jar包下载地址:https://sourceforge.net/projects/fastjson/下载完成后需要把jar包拷贝到WEB-INF/lib文件夹中.2)使用pom ...
- springmvc与fastjson的整合,注解@RequestBody的使用
项目内容用的是jetty框架,传输数据格式是json格式,有一天我心血来潮,把项目又搭建了一次,完了,卡在了数据传输的格式上,明明原来框架直接用fastjson,但是我用就是不对,总是报fastjso ...
- SpringMVC整合fastjson、easyui 乱码问题
一.框架版本 SpringMVC:3.1.1.RELEASE fastjson:1.2.7 easyui :1.4.5 二.乱码现象 Action中使用@ResponseBody返回Json数据 ...
- SpringMVC使用fastjson自定义Converter支持返回jsonp格式(转)
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import c ...
- 描述下fastJSON,jackson等等的技术
①Jackson:依赖的jar包较少,简单易用性能高,更新速度也比较快,但是对于复杂类型的json转换bean会出 现问题,一些集合Map,List的转换出现问题,对于复杂类型的bean转换Json, ...
随机推荐
- Unity 数据Json格式的转换
把对象转换为字节序列的过程称为对象的序列化. 把字节序列化恢复为对象过程称为对象的反序列化. JSON格式的转换,是一大神给我说的,让我拿来存储数据库时对一些数据的处理,感觉特别好用.但是我并没有深入 ...
- EBS initialization parameters - Healthcheck
APPLIES TO: Oracle EBS Applications Performance - Version 11.5.10.2 to 12.2 [Release 11.5.10 to 12.2 ...
- 【Qt编程】基于Qt的词典开发系列<四>--无边框窗口的缩放与拖动
在现在,绝大多数软件都向着简洁,时尚发展.就拿有道的单词本和我做的单词本来说,绝大多数用户肯定喜欢我所做的单词本(就单单界面,关于颜色搭配和布局问题,大家就不要在意了). 有道的单词本: 我所做的单词 ...
- Xcode使用心得02:如何在项目中关闭ARC特性
在obj-c系列内存管理的博文里大家应该对ARC有所了解,一般是不推荐关闭ARC特性的,但你也保不齐啥时候有这个需求,于是乎我们看看在最新的x6b中如何将其关闭吧. 因为Build Seting里的子 ...
- ethtool确定网卡对应的物理网口
在配置有多个网络接口的设备时我们会犯难,eth0.eth1.--到底是那个接口? 我使用的机器是CentOS系统,打开终端,输入ethtool –help 显示帮助信息,下面我就简要介绍一下最常用的两 ...
- SpringBoot yml 配置
1. 在 spring boot 中,有两种配置文件,一种是application.properties,另一种是application.yml,两种都可以配置spring boot 项目中的一些变量 ...
- 对DB2常见错误的列举以及破解方案
我们今天主要描述的是DB2常见错误还有正对这些错误的解决方案,以下就是文章对DB2常见错误还有正对这些错误的解决方案的主要内容的详细描述. 以下的文章主要是介绍DB2常见错误还有正对这些错误的解决方案 ...
- MVC3 项目总结
验证 Validation 多样化验证规则 http://www.cnblogs.com/xling/archive/2012/07/11/2587002.html 最常见的验证方式是:在实体的属性上 ...
- 编码与Python的基础
编码 在linux 系统或者Python2版本中要用Python这门语言呢,就需要在开头加上 # -*- coding:utf8 -*- 这个语句是说呀,当机器编译你写的程序的时候是用utf-8这种编 ...
- Java内存模型与指令重排
Java内存模型与指令重排 本文暂不讲JMM(Java Memory Model)中的主存, 工作内存以及数据如何在其中流转等等, 这些本身还牵扯到硬件内存架构, 直接上手容易绕晕, 先从以下几个点探 ...