Jackson序列化(7) —使用BeanSerializerModifier定義Jackson的序列化方式(對(duì)于null现柠,String格式要返回"")

需求:返回前端的JSON報(bào)文,對(duì)于null弛矛,String格式要返回""够吩,Number格式需要返回0,array格式需要返回[]丈氓,boolean類(lèi)型需要返回false周循。

最終的解決方案是使用自定義BeanSerializerModifier來(lái)影響序列化方式。

1. 源碼分析

Jackson依賴(lài)BeanPropertyWriter對(duì)象對(duì)每一個(gè)屬性進(jìn)行序列化和反序列化万俗。
對(duì)于null值的序列化和反序列化湾笛,提供了com.fasterxml.jackson.databind.ser.BeanPropertyWriter#_nullSerializer序列化類(lèi)。

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen,
            SerializerProvider prov) throws Exception {
        // inlined 'get()'
        final Object value = (_accessorMethod == null) ? _field.get(bean)
                : _accessorMethod.invoke(bean, (Object[]) null);

        // Null handling is bit different, check that first
        if (value == null) {
            if (_nullSerializer != null) {
                gen.writeFieldName(_name);
                //使用null值序列化類(lèi)來(lái)實(shí)現(xiàn)
                _nullSerializer.serialize(null, gen, prov);
            }
            return;
        }
      ...
      }

那么如何修改BeanPropertyWriter中的_nullSerializer呢该编?

public class BeanSerializerFactory
    extends BasicSerializerFactory
    implements java.io.Serializable // since 2.1
{
    protected JsonSerializer<Object> constructBeanOrAddOnSerializer(SerializerProvider prov,
            JavaType type, BeanDescription beanDesc, boolean staticTyping)
        throws JsonMappingException
    {
      ...
        // 獲取到每個(gè)屬性的BeanPropertyWriter集合對(duì)象
        List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
        if (props == null) {
            props = new ArrayList<BeanPropertyWriter>();
        } else {
            props = removeOverlappingTypeIds(prov, beanDesc, builder, props);
        }
        
        // [databind#638]: Allow injection of "virtual" properties:
        prov.getAnnotationIntrospector().findAndAddVirtualProperties(config, beanDesc.getClassInfo(), props);

        // [JACKSON-440] Need to allow modification bean properties to serialize:
        if (_factoryConfig.hasSerializerModifiers()) {
            //遍歷所有自定義的BeanSerializerModifier對(duì)象迄本,修改props的集合
            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
                props = mod.changeProperties(config, beanDesc, props);
            }
        }
       ...
    }
}

思路:我們可以修改BeanSerializerFactory對(duì)象中的_factoryConfig屬性,添加自定義serializerModifiers對(duì)象课竣。從而影響對(duì)象List<BeanPropertyWriter>嘉赎。替換BeanPropertyWriter對(duì)象中的_nullSerializer屬性,實(shí)現(xiàn)自定義的serialize方法于樟。

2. 實(shí)現(xiàn)

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

import java.util.*;

public class CustomizeBeanSerializerModifier extends BeanSerializerModifier {

    public static final String ARRAY_TYPE = "array";
    public static final String STRING_TYPE = "string";
    public static final String BOOLEAN_TYPE = "boolean";
    public static final String NUMBER_TYPE = "number";
    public static final String OBJECT_TYPE = "object";

    public static final Map<String, JsonSerializer<Object>> map = new HashMap<String, JsonSerializer<Object>>() {{
        put(ARRAY_TYPE, new CustomizeNullJsonSerializer.NullArrayJsonSerializer());
        put(STRING_TYPE, new CustomizeNullJsonSerializer.NullStringJsonSerializer());
        put(BOOLEAN_TYPE, new CustomizeNullJsonSerializer.NullBooleanJsonSerializer());
        put(NUMBER_TYPE, new CustomizeNullJsonSerializer.NullNumberJsonSerializer());
        put(OBJECT_TYPE, new CustomizeNullJsonSerializer.NullObjectJsonSerializer());
    }};


    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
        //替換內(nèi)部的方法
        for(BeanPropertyWriter beanPropertyWriter:beanProperties){
            String type = getType(beanPropertyWriter);
            //獲取對(duì)應(yīng)類(lèi)型的空值轉(zhuǎn)換器
            JsonSerializer<Object> objectJsonSerializer = map.get(type);
            if(objectJsonSerializer!=null){
                //分配空值轉(zhuǎn)換器
                beanPropertyWriter.assignNullSerializer(objectJsonSerializer);
            }
        }
        return beanProperties;
    }

    public String getType(BeanPropertyWriter p) {
        if (isArrayType(p)) {
            return ARRAY_TYPE;
        }

        if (isStringType(p)) {
            return STRING_TYPE;
        }

        if (isBooleanType(p)) {
            return BOOLEAN_TYPE;
        }

        if (isNumberType(p)) {
            return NUMBER_TYPE;
        }
        return null;
    }

    /**
     * 是否是數(shù)組
     */
    private boolean isArrayType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是String
     */
    private boolean isStringType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是數(shù)值類(lèi)型
     */
    private boolean isNumberType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return Number.class.isAssignableFrom(clazz);
    }

    /**
     * 是否是boolean
     */
    private boolean isBooleanType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getType().getRawClass();
        return clazz.equals(Boolean.class);
    }

}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class CustomizeNullJsonSerializer {

    /**
     * 數(shù)組集合類(lèi)
     */
    public static class NullArrayJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeStartArray();
            gen.writeEndArray();
        }
    }

    /**
     * String
     */
    public static class NullStringJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeString("");
        }
    }

    /**
     * Number
     */
    public static class NullNumberJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeNumber(0);
        }
    }

    /**
     * Boolean
     */
    public static class NullBooleanJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeBoolean(false);
        }
    }


    /**
     * Object
     */
    public static class NullObjectJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeStartObject();
            gen.writeEndObject();
        }
    }


}

測(cè)試代碼:

public class TestJackson {

    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();

       objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new CustomizeBeanSerializerModifier()));


        OrderDto orderDto=new OrderDto();


        try {
            String s = objectMapper.writeValueAsString(orderDto);
            System.out.println(s);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }


    }
}

3. 修改@RequestBody和@ResponseBody的序列化方式

加入斷點(diǎn):org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

public class ContentMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    public ContentMappingJackson2HttpMessageConverter() {
        super();
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new CustomizeBeanSerializerModifier()));
    }

    //決定是否使用該類(lèi)作為消息轉(zhuǎn)換器進(jìn)行序列化公条。
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return super.canWrite(clazz, mediaType);
    }
}

優(yōu)先級(jí)最高,注意不要使用@EnableWebMvc注解迂曲。

@Configuration
public class MessageConverterConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(0, new ContentMappingJackson2HttpMessageConverter());
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靶橱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子路捧,更是在濱河造成了極大的恐慌关霸,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杰扫,死亡現(xiàn)場(chǎng)離奇詭異队寇,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)章姓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén)佳遣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人凡伊,你說(shuō)我怎么就攤上這事零渐。” “怎么了系忙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵诵盼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)拦耐,這世上最難降的妖魔是什么耕腾? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮杀糯,結(jié)果婚禮上扫俺,老公的妹妹穿的比我還像新娘。我一直安慰自己固翰,他們只是感情好狼纬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著骂际,像睡著了一般疗琉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上歉铝,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天盈简,我揣著相機(jī)與錄音,去河邊找鬼太示。 笑死柠贤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的类缤。 我是一名探鬼主播臼勉,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼餐弱!你這毒婦竟也來(lái)了宴霸?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤膏蚓,失蹤者是張志新(化名)和其女友劉穎瓢谢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體驮瞧,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氓扛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剧董。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幢尚。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡破停,死狀恐怖翅楼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情真慢,我是刑警寧澤毅臊,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站黑界,受9級(jí)特大地震影響管嬉,放射性物質(zhì)發(fā)生泄漏皂林。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一蚯撩、第九天 我趴在偏房一處隱蔽的房頂上張望础倍。 院中可真熱鬧,春花似錦胎挎、人聲如沸沟启。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)德迹。三九已至,卻和暖如春揭芍,著一層夾襖步出監(jiān)牢的瞬間胳搞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工称杨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肌毅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓列另,卻偏偏與公主長(zhǎng)得像芽腾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子页衙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

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