jackson自定義序列化中null值的處理

參考:【私人定制jackson】定制jackson的自定義序列化(null值的處理)

在springboot中使用jackson,返回json數據時輸出null值時按照屬性類型輸出具體形式

可以配置其一個MappingJackson2HttpMessageConverter類,這個類同時可以做另一個事情,防止ie對json數據當做文件進行下載。

MappingJackson2HttpMessageConverter類中可以取到一個ObjectMapper,即jackson序列化的主類。

@Configuration

class JacksonHttpMessageConvertersConfiguration {

@Configuration

@ConditionalOnClass(ObjectMapper.class)

@ConditionalOnBean(ObjectMapper.class)

@ConditionalOnProperty(name= HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,havingValue="jackson",matchIfMissing=true)

protected static class MappingJackson2HttpMessageConverterConfiguration {

? @Bean

? ?@ConditionalOnMissingBean(value=? ? ? MappingJackson2HttpMessageConverter.class,ignoredType= {

"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",

"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter"})

public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(

ObjectMapper objectMapper) {

return new? MappingJackson2HttpMessageConverter(objectMapper);

? ? ? ?}

? ? }

}

上面的代碼是springboot自帶的配置文件,在packageorg.springframework.boot.autoconfigure.web包中

而我們可應通過自己配置的ObjectMapper來異化jackson在具體操作中解決null輸出的類型

在MappingJackson2HttpMessageConverter中最為重要的就是writeInternal方法,代碼如下

@Override

protected void writeInternal(Object object, HttpOutputMessage outputMessage)

throws IOException, HttpMessageNotWritableException {

JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());

// The following has been deprecated as late as Jackson 2.2 (April 2013);

// preserved for the time being, for Jackson 2.0/2.1 compatibility.

@SuppressWarnings("deprecation")

JsonGenerator jsonGenerator =

this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);

// A workaround for JsonGenerators not applying serialization features

// https://github.com/FasterXML/jackson-databind/issues/12

if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {

jsonGenerator.useDefaultPrettyPrinter();

? }

try {

if (this.jsonPrefix != null) {

jsonGenerator.writeRaw(this.jsonPrefix);

? ? }

//此處進行序列化

this.objectMapper.writeValue(jsonGenerator, object);

? ? }catch (JsonProcessingException ex) {

throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);

? ? ?}

}

可以發現在writeInternal方法中使用了this.objectMapper.writeValue(jsonGenerator, object);

進行序列化,跟進去,看到一句話:

_serializerProvider(config).serializeValue(jgen, value);

看來這個就是具體的序列化的方法了。

public void serializeValue(JsonGenerator jgen, Object value)? ? ? ? throws IOException, JsonGenerationException? ? {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (value == null) {? ? ? ? ? ? _serializeNull(jgen);? ? ? ? ? ? return;? ? ? ? }? ? ? ?

? ? ? ? Class cls = value.getClass();? ? ? ? // true, since we do want to cache root-level typed? ? ? ? ? ? ? ? ? ? ? ? ? ?serializers (ditto for null property)? ? ? ? final JsonSerializerser = findTypedValueSerializer(cls, true, null);? ? ?

try {

? ? ? ? ser.serialize(value, jgen, this);

? ?} catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is

? throw ioe;

? ?} catch (Exception e) { // but wrap RuntimeExceptions, to get path information

String msg = e.getMessage();

if (msg == null) {

msg = "[no message for "+e.getClass().getName()+"]";

? ?}

throw new JsonMappingException(msg, e);

? }

}

在這里我們找到了對于null處理的方法

if (value == null) {? ? ? ? ? ? _serializeNull(jgen);? ? ? ? ? ? return;? ? ? ? }

繼續跟進_serializeNull(jgen)

protected void_serializeNull(JsonGenerator gen)throwsIOException

{

JsonSerializer ser = getDefaultNullValueSerializer();

try{

ser.serialize(null,gen, this);

? ? }catch(IOException ioe) {// no wrapping for IO (and derived)

throwioe;

? ? }catch(Exception e) {// but others do need to be, to get path etc

String msg = e.getMessage();

if(msg ==null) {

? ? msg ="[no message for "+e.getClass().getName()+"]";

? }? ?

reportMappingProblem(e,msg);

? ?}

}

發現JsonSerializer ser = getDefaultNullValueSerializer()

JsonSerializer是一個抽象類,具有多個實現

public JsonSerializer getDefaultNullValueSerializer() {

return _nullValueSerializer;

}

ser.serialize(null,gen, this);

而在_nullValueSerializer中ser的具體實現是這樣的

@Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public? void? serialize(Object value, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException

{

jgen.writeNull();

}

只要替換掉這個_nullValueSerializer? 就可以了。

但是這個jsonSerializer有一個比較嚴重的問題,就是這個nullValueSerializer是全局的,即所有的null都會應用這個JsonSerializer,在這個類中無法判斷類型。

所以繼續向下跟代碼:

跟入 ser.serialize(value, jgen, this); ?這個方法,發現其有許多的實現,通過調試模式,進入了一個叫做BeanSerializer的類,其實現為:

/*** Main serialization method that will delegate actual output to

* configured

* {@linkBeanPropertyWriter} instances.*/

@Overridepublicfinalvoidserialize(Object bean, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException

{

if(_objectIdWriter !=null) {

_serializeWithObjectId(bean, jgen, provider,true);return;

?? }

jgen.writeStartObject();if(_propertyFilterId !=null) {

serializeFieldsFiltered(bean, jgen, provider);

?? }else{

//調試模式下最終走了這個方法? ? ? ? ??

serializeFields(bean, jgen, provider);? ? ??

??? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? jgen.writeEndObject();?

? }

protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException

{finalBeanPropertyWriter[] props;if(_filteredProps !=null&& provider.getActiveView() !=null) {

props=_filteredProps;

? }else{

props=_props;

?? }inti = 0;try{for(finalintlen = props.length; i < len; ++i) {

BeanPropertyWriter prop=props[i];

if(prop !=null) {//can have nulls in filtered list

prop.serializeAsField(bean, jgen, provider);

? }

?? }if(_anyGetterWriter !=null) {

_anyGetterWriter.getAndSerialize(bean, jgen, provider);

?? }

?? }catch(Exception e) {

String name= (i == props.length) ? "[anySetter]": props[i].getName();

wrapAndThrow(provider, e, bean, name);

?? }catch(StackOverflowError e) {/*04-Sep-2009, tatu: Dealing with this is tricky, since we do not

*? have many stack frames to spare... just one or two; can't

*? make many calls.*/JsonMappingException mapE=newJsonMappingException("Infinite recursion (StackOverflowError)", e);

String name= (i == props.length) ? "[anySetter]": props[i].getName();

mapE.prependPath(newJsonMappingException.Reference(bean, name));throwmapE;

?? }

}

這個方法中最重要的一個東西就是BeanPropertyWriter 這個類,這個類是由SerializerFactory 工廠進行實例化的,其作用是對bean中的每個字段進行jackson操作的封裝,其中封裝了字段的一些元信息,和對此字段進行jackson序列化的操作,那么問題來了,這么說來,這個BeanPropertyWriter類其實就是jackson真正如何對每個bean進行轉json的最終的操作的實現,那么我們是不是只要替換掉這個類就可以了。那么看看jackson為我們預留的對此類進行自定義的方法。

jackson通過JsonSerializer來對javabean序列化,此serializer都是通過一個SerializerFactory活的的,在這個工廠類中,找到了一個這個方法:

@SuppressWarnings("unchecked")

protected JsonSerializer constructBeanSerializer(SerializerProvider prov,

BeanDescription beanDesc)throwsJsonMappingException

{//13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object//05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?

if(beanDesc.getBeanClass() == Object.class) {returnprov.getUnknownTypeSerializer(Object.class);

//throw new IllegalArgumentException("Can not create bean serializer for Object.class");}

finalSerializationConfig config =prov.getConfig();

BeanSerializerBuilder builder=constructBeanSerializerBuilder(beanDesc);

builder.setConfig(config);

//First: any detectable (auto-detect, annotations) properties to serialize?

//注意這里,這里為每個屬性實例化了一個BeanPropertyWriter? ? ? ?

List props =findBeanProperties(prov, beanDesc, builder);

if(props ==null) {

props=newArrayList();

}//[JACKSON-440] Need to allow modification bean properties to serialize:

//這里通過_factoryConfig中的配置:BeanSerializerModifier 對這個props做了change(修改),

if(_factoryConfig.hasSerializerModifiers()) {for(BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {

props=mod.changeProperties(config, beanDesc, props);

}

}//Any properties to suppress?props = filterBeanProperties(config, beanDesc, props);

//.....之后的省略

重點注意:

//這里通過_factoryConfig中的配置:? ?BeanSerializerModifier 對這個props做了change(修改),

if (_factoryConfig.hasSerializerModifiers()) {

for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()){

props =mod.changeProperties(config, beanDesc, props);

? }

}

這里從factoryConfig中拿出來了一個Modifiers集合,并且通過這些Modifiers對List進行了修改,那么這樣就簡單了,我們只要自己定義一個Modifyer對某個List類型的BeanPropertyWriter進行修改集合了。

首先定義一個Modifyer

public class MyBeanSerializerModifier extends BeanSerializerModifier {

private JsonSerializer _nullArrayJsonSerializer =new MyNullArrayJsonSerializer();

@Override

public? List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,

List beanProperties) {

//循環所有的beanPropertyWriter

for(int i = 0; i < beanProperties.size(); i++) {

BeanPropertyWriter writer=beanProperties.get(i);

//判斷字段的類型,如果是array,list,set則注冊nullSerializer

if(isArrayType(writer)) {

//給writer注冊一個自己的nullSerializer? ? ? ? ? ? ? ? writer.assignNullSerializer(this.defaultNullArrayJsonSerializer());

? }

?? }

??? return beanProperties;

?? }

? //判斷是什么類型

protected boolean isArrayType(BeanPropertyWriter writer) {

Class clazz =writer.getPropertyType();

return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class);

? }

protected? JsonSerializer<Object>? defaultNullArrayJsonSerializer() {

??? return? _nullArrayJsonSerializer;

? }

}

一個對null值處理的JsonSeralizer:

publicclass MyNullArrayJsonSerializer extends JsonSerializer{

@Override

public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonProcessingException {if(value ==null) {

jgen.writeStartArray();

jgen.writeEndArray();

?? }else{

?? jgen.writeObject(value);

?? }

? }

}


還是那個MappingJackson2HttpMessageConverter:


@Configuration

public classJsonConfig {

@Bean

publicMappingJackson2HttpMessageConvertermappingJacksonHttpMessageConverter() {

final MappingJackson2HttpMessageConverter converter = new? MappingJackson2HttpMessageConverter();

ObjectMapper mapper = converter.getObjectMapper();

// 為mapper注冊一個帶有SerializerModifier的Factory,此modifier主要做的事情為:當序列化類型為array,list、set時,當值為空時,序列化成[]

mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(newMyBeanSerializerModifier()));

returnconverter;

? }

}

大功告成!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,948評論 18 139
  • 文章作者:Tyan博客:noahsnail.com 3.5 Bean scopes When you create...
    SnailTyan閱讀 1,917評論 0 1
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,766評論 18 399
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,201評論 2 7
  • (一) 不知道從什么時候開始,卻一直延續至今的習慣——在沒有星星的夜晚一遍又一遍地思考自己到底該以怎樣的姿態存在于...
    祁泠泠閱讀 268評論 2 0