【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有完善的擴展機制。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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM