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

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

在springboot中使用jackson留攒,返回json數(shù)據(jù)時(shí)輸出null值時(shí)按照屬性類型輸出具體形式

可以配置其一個(gè)MappingJackson2HttpMessageConverter類会油,這個(gè)類同時(shí)可以做另一個(gè)事情,防止ie對(duì)json數(shù)據(jù)當(dāng)做文件進(jìn)行下載。

MappingJackson2HttpMessageConverter類中可以取到一個(gè)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包中

而我們可應(yīng)通過(guò)自己配置的ObjectMapper來(lái)異化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);

? ? }

//此處進(jìn)行序列化

this.objectMapper.writeValue(jsonGenerator, object);

? ? }catch (JsonProcessingException ex) {

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

? ? ?}

}

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

進(jìn)行序列化朽缴,跟進(jìn)去,看到一句話:

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

看來(lái)這個(gè)就是具體的序列化的方法了水援。

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);

? }

}

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

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

繼續(xù)跟進(jìn)_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);

? ?}

}

發(fā)現(xiàn)JsonSerializer ser = getDefaultNullValueSerializer()

JsonSerializer是一個(gè)抽象類密强,具有多個(gè)實(shí)現(xiàn)

public JsonSerializer getDefaultNullValueSerializer() {

return _nullValueSerializer;

}

ser.serialize(null,gen, this);

而在_nullValueSerializer中ser的具體實(shí)現(xiàn)是這樣的

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

{

jgen.writeNull();

}

只要替換掉這個(gè)_nullValueSerializer? 就可以了。

但是這個(gè)jsonSerializer有一個(gè)比較嚴(yán)重的問(wèn)題蜗元,就是這個(gè)nullValueSerializer是全局的或渤,即所有的null都會(huì)應(yīng)用這個(gè)JsonSerializer,在這個(gè)類中無(wú)法判斷類型奕扣。

所以繼續(xù)向下跟代碼:

跟入 ser.serialize(value, jgen, this); ?這個(gè)方法薪鹦,發(fā)現(xiàn)其有許多的實(shí)現(xiàn),通過(guò)調(diào)試模式,進(jìn)入了一個(gè)叫做BeanSerializer的類池磁,其實(shí)現(xiàn)為:

/*** 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{

//調(diào)試模式下最終走了這個(gè)方法? ? ? ? ??

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;

?? }

}

這個(gè)方法中最重要的一個(gè)東西就是BeanPropertyWriter 這個(gè)類奔害,這個(gè)類是由SerializerFactory 工廠進(jìn)行實(shí)例化的,其作用是對(duì)bean中的每個(gè)字段進(jìn)行jackson操作的封裝地熄,其中封裝了字段的一些元信息华临,和對(duì)此字段進(jìn)行jackson序列化的操作,那么問(wèn)題來(lái)了端考,這么說(shuō)來(lái)雅潭,這個(gè)BeanPropertyWriter類其實(shí)就是jackson真正如何對(duì)每個(gè)bean進(jìn)行轉(zhuǎn)json的最終的操作的實(shí)現(xiàn),那么我們是不是只要替換掉這個(gè)類就可以了跛梗。那么看看jackson為我們預(yù)留的對(duì)此類進(jìn)行自定義的方法寻馏。

jackson通過(guò)JsonSerializer來(lái)對(duì)javabean序列化,此serializer都是通過(guò)一個(gè)SerializerFactory活的的核偿,在這個(gè)工廠類中诚欠,找到了一個(gè)這個(gè)方法:

@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?

//注意這里,這里為每個(gè)屬性實(shí)例化了一個(gè)BeanPropertyWriter? ? ? ?

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

if(props ==null) {

props=newArrayList();

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

//這里通過(guò)_factoryConfig中的配置:BeanSerializerModifier 對(duì)這個(gè)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);

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

重點(diǎn)注意:

//這里通過(guò)_factoryConfig中的配置:? ?BeanSerializerModifier 對(duì)這個(gè)props做了change(修改)轰绵,

if (_factoryConfig.hasSerializerModifiers()) {

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

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

? }

}

這里從factoryConfig中拿出來(lái)了一個(gè)Modifiers集合,并且通過(guò)這些Modifiers對(duì)List進(jìn)行了修改尼荆,那么這樣就簡(jiǎn)單了左腔,我們只要自己定義一個(gè)Modifyer對(duì)某個(gè)List類型的BeanPropertyWriter進(jìn)行修改集合了。

首先定義一個(gè)Modifyer

public class MyBeanSerializerModifier extends BeanSerializerModifier {

private JsonSerializer _nullArrayJsonSerializer =new MyNullArrayJsonSerializer();

@Override

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

List beanProperties) {

//循環(huán)所有的beanPropertyWriter

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

BeanPropertyWriter writer=beanProperties.get(i);

//判斷字段的類型捅儒,如果是array液样,list,set則注冊(cè)nullSerializer

if(isArrayType(writer)) {

//給writer注冊(cè)一個(gè)自己的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;

? }

}

一個(gè)對(duì)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);

?? }

? }

}


還是那個(gè)MappingJackson2HttpMessageConverter:


@Configuration

public classJsonConfig {

@Bean

publicMappingJackson2HttpMessageConvertermappingJacksonHttpMessageConverter() {

final MappingJackson2HttpMessageConverter converter = new? MappingJackson2HttpMessageConverter();

ObjectMapper mapper = converter.getObjectMapper();

// 為mapper注冊(cè)一個(gè)帶有SerializerModifier的Factory巧还,此modifier主要做的事情為:當(dāng)序列化類型為array鞭莽,list、set時(shí)麸祷,當(dāng)值為空時(shí)澎怒,序列化成[]

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

returnconverter;

? }

}

大功告成!阶牍!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喷面,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子走孽,更是在濱河造成了極大的恐慌惧辈,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,185評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磕瓷,死亡現(xiàn)場(chǎng)離奇詭異盒齿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)县昂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人陷舅,你說(shuō)我怎么就攤上這事倒彰。” “怎么了莱睁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,684評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵待讳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我仰剿,道長(zhǎng)创淡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,564評(píng)論 1 284
  • 正文 為了忘掉前任南吮,我火速辦了婚禮琳彩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘部凑。我一直安慰自己露乏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,681評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布涂邀。 她就那樣靜靜地躺著瘟仿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪比勉。 梳的紋絲不亂的頭發(fā)上劳较,一...
    開(kāi)封第一講書(shū)人閱讀 49,874評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音浩聋,去河邊找鬼观蜗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赡勘,可吹牛的內(nèi)容都是我干的嫂便。 我是一名探鬼主播,決...
    沈念sama閱讀 39,025評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼闸与,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼毙替!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起践樱,我...
    開(kāi)封第一講書(shū)人閱讀 37,761評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤厂画,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拷邢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體袱院,經(jīng)...
    沈念sama閱讀 44,217評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,545評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忽洛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腻惠。...
    茶點(diǎn)故事閱讀 38,694評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖欲虚,靈堂內(nèi)的尸體忽然破棺而出集灌,到底是詐尸還是另有隱情,我是刑警寧澤复哆,帶...
    沈念sama閱讀 34,351評(píng)論 4 332
  • 正文 年R本政府宣布欣喧,位于F島的核電站,受9級(jí)特大地震影響梯找,放射性物質(zhì)發(fā)生泄漏唆阿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,988評(píng)論 3 315
  • 文/蒙蒙 一锈锤、第九天 我趴在偏房一處隱蔽的房頂上張望驯鳖。 院中可真熱鬧,春花似錦牙咏、人聲如沸臼隔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,778評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)摔握。三九已至,卻和暖如春丁寄,著一層夾襖步出監(jiān)牢的瞬間氨淌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,007評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工伊磺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盛正,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,427評(píng)論 2 360
  • 正文 我出身青樓屑埋,卻偏偏與公主長(zhǎng)得像豪筝,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摘能,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,580評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理续崖,服務(wù)發(fā)現(xiàn),斷路器团搞,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 文章作者:Tyan博客:noahsnail.com 3.5 Bean scopes When you create...
    SnailTyan閱讀 1,882評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法严望,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法逻恐,繼承相關(guān)的語(yǔ)法像吻,異常的語(yǔ)法峻黍,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,598評(píng)論 18 399
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,135評(píng)論 2 7
  • (一) 不知道從什么時(shí)候開(kāi)始,卻一直延續(xù)至今的習(xí)慣——在沒(méi)有星星的夜晚一遍又一遍地思考自己到底該以怎樣的姿態(tài)存在于...
    祁泠泠閱讀 263評(píng)論 2 0