欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot序列化和反序列化器配置方法

 更新時間:2023年08月01日 11:16:20   作者:wangfenglei123456  
這篇文章主要介紹了springboot序列化和反序列化器配置方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

介紹

本文介紹在項(xiàng)目中時間類型、枚舉類型的序列化和反序列化自定義的處理類,也可以使用注解。

建議枚舉都實(shí)現(xiàn)一個統(tǒng)一的接口,方便處理。我這定義了一個Dict接口。

枚舉類型注解處理

這種方式比較靈活,可以讓枚舉按照自己的方式序列化,可以序列化code值(推薦),也可以序列化對象。序列化為對象:對于前端來說是比較好的,前端接收到code值和中文label,頁面顯示label就行了,但是對于后端,服務(wù)之間調(diào)用,使用feign調(diào)用的時候,序列化為對象,被調(diào)用服務(wù)的controller層就接收不到這個枚舉值了,因?yàn)榻邮盏氖敲杜e序列化的對象,無法反序列化成枚舉對象了。除非使用自定義反序列加判斷去處理,比較麻煩。參考改進(jìn)枚舉工具類

定義統(tǒng)一枚舉接口

package com.common.interfaces;
public interface Dict {
    String name();
    default Integer getCode() {
        return null;
    }
    default String getLabel() {
        return null;
    }
}

序列化code值

枚舉代碼

package com.common.enums.app;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.common.interfaces.Dict;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum DeliverDateModelEnum implements Dict {
    SAME(0, "相同時間"),
    DIFFERENT(2, "不同時間"),
    ;
    // mybatis的處理注解                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
    @EnumValue
    // Jackson的序列化處理注解;序列化code值
    @JsonValue
    private final Integer code;
    private final String label;
}
package com.common.enums.order;
import com.common.exception.E;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * 訂單采購方式枚舉
 */
@Getter
@AllArgsConstructor
public enum OrderPurchaseMethodEnum implements Dict {
    INQUIRY("詢價"),
    MALL("商城"),
    ;
    private String label;
    private static final Map<String, OrderPurchaseMethodEnum> MAP =
            Arrays.stream(OrderPurchaseMethodEnum.values()).collect(Collectors.toMap(Enum::name, e -> e));
    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static OrderPurchaseMethodEnum resolve(String code) {
        if (!MAP.containsKey(code)) {
            throw new Exception("枚舉類型錯誤");
        }
        return MAP.get(code);
    }
}

實(shí)體類

package com.app.dto;
import com.common.enums.apply.DeliverDateModelEnum;
import com.common.enums.order.OrderPurchaseMethodEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApplyInfoDTO2 implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主鍵")
    private Long id;
    @ApiModelProperty(value = "標(biāo)題", required = true)
    private String title;
   @NotNull(message = "時間方式不能為空")
    private DeliverDateModelEnum deliverDateModel;
// 后期會用到
    private OrderPurchaseMethodEnum purchaseMethod;
}

測試代碼

@RestController
@Slf4j
@RequestMapping("/api/test")
public class TestController2 {
    @PostMapping(value = "/hh")
    public ApplyInfoDTO2 test9(@RequestBody ApplyInfoDTO2 applyInfoDTO) {
        System.out.println("hhhhh");
        applyInfoDTO.setId(55L);
        System.out.println("----"+ JacksonUtils.toJson(applyInfoDTO));
        return applyInfoDTO;
    }
}
請求參數(shù)
{
    "id":11,
    "title": "dajf",
    "deliverDateModel":2
}
響應(yīng)結(jié)果
{
    "id": 55,
    "title": "dajf",
    "deliverDateModel": 2,
    "purchaseMethod": null
}

序列化對象

這里只改枚舉就可以了

package com.common.enums.app;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.common.interfaces.Dict;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum DeliverDateModelEnum implements Dict {
    SAME(0, "相同時間"),
    DIFFERENT(2, "不同時間"),
    ;
    // mybatis的處理注解
    @EnumValue
    // 由于上邊注解標(biāo)記序列化為對象了,這就不起作用了,可以注掉
    // @JsonValue
    private final Integer code;
    private final String label;
}

結(jié)果

請求參數(shù)
{
    "id":11,
    "title": "dajf",
    "deliverDateModel":0
}
 
響應(yīng)結(jié)果
{
    "id": 55,
    "title": "dajf",
    "deliverDateModel": {
        "code": 0,
        "label": "相同時間"
    },
    "purchaseMethod": null
}

反序列化處理

這就是說,前端傳code值0,后端可以對應(yīng)到枚舉字段上,默認(rèn)的是使用下標(biāo)ordinal來反序列化的,按照0,1,2,3...去對應(yīng)上,如果中跳過了,接收值的時候就會報(bào)錯。比如上邊的枚舉類DeliverDateModelEnum,傳0不會報(bào)錯,傳2就找不到了,錯誤如下,

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.common.enums.apply.DeliverDateModelEnum` from number 2: index value outside legal index range [0..1]
 at [Source: (PushbackInputStream); line: 4, column: 21] (through reference chain: com.cnpc.app.dto.ApplyInfoDTO2["deliverDateModel"])

解決方案:在枚舉類加反序列化處理代碼@JsonCreator

package com.common.enums.app;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.common.interfaces.Dict;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum DeliverDateModelEnum implements Dict {
    SAME(0, "相同時間"),
    DIFFERENT(2, "不同時間"),
    ;
    // mybatis的處理注解                      
    @EnumValue
    // 由于上邊注解標(biāo)記序列化為對象了,這就不起作用了,可以注掉
    // @JsonValue
    private final Integer code;
    private final String label;
    private static final Map<Integer, DeliverDateModelEnum> map =
            Arrays.stream(DeliverDateModelEnum.values()).collect(Collectors.toMap(DeliverDateModelEnum::getCode, e -> e));
    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static DeliverDateModelEnum resolve(Integer code) {
        if (!map.containsKey(code)) {
            throw new E("找不到枚舉");
        }
        return map.get(code);
    }
}

以上就是注解的枚舉處理方法,統(tǒng)一定義接口后,為了前端可以通過code值展示枚舉中午label,所以又寫了一個字典解析類,將所以實(shí)現(xiàn)Dict接口的枚舉,都拿到,然后解析存儲,再給前端提供一個請求路徑。前端存儲起來,去解析,退出的時候,可以清除前端緩存。

將枚舉轉(zhuǎn)字典存儲

package com.dict.service;
import com.common.annotation.DictType;
import com.common.exception.E;
import com.common.interfaces.Dict;
import com.dict.vo.DictItemVO;
import com.dict.vo.DictVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 系統(tǒng)字典Service
 */
@Slf4j
@Component
public class SystemDictService {
    /**
     * 反射要掃描的包路徑
     */
    @Value("${dict.scanPath:com.company}")
    private String scanPath;
    /**
     * 字典集合
     */
    private static Set<DictVO> DICT_SET = new HashSet<>();
    /**
     * 提供給外部獲取字典集合的方法
     *
     * @author sun
     */
    public Set<DictVO> getDictSet() {
        return DICT_SET;
    }
    /**
     * 啟動時掃描
     */
    @PostConstruct
    public void initDict() {
        DICT_SET = scanDict();
    }
    /**
     * 掃描字典列表
     */
    private Set<DictVO> scanDict() {
        // 反射要掃描的包路徑
        Reflections reflections = new Reflections(scanPath);
        // 反射獲取所有實(shí)現(xiàn) DictInterface 接口的枚舉
        Set<Class<? extends Dict>> monitorClasses = reflections.getSubTypesOf(Dict.class);
        /*
         * 封裝字典列表
         * 過濾掉不是枚舉的實(shí)現(xiàn)
         * 反射調(diào)用枚舉的 values 方法, 獲取每個枚舉內(nèi)部的值列表
         */
        return monitorClasses.stream()
                .filter(Class::isEnum)
                .map(sub -> {
                    DictVO vo = new DictVO();
                    try {
                       /* 這塊沒有使用到
                        DictType annotation = sub.getAnnotation(DictType.class);
                        if (Objects.nonNull(annotation) && Strings.isNotBlank(annotation.type())) {
                            // 有DictType注解并且type不是空白時使用注解中的值作為字典的類別
                            vo.setType(annotation.type());
                        } else {
                            // 否則使用類名作為字典的類別
                            vo.setType(sub.getSimpleName());
                        }*/
                        Method valuesMethod = sub.getMethod("values");
                        Object valuesObj = valuesMethod.invoke(sub);
                        Dict[] values = (Dict[]) valuesObj;
                        List<DictItemVO> collect = Arrays.stream(values)
                                .map(item -> {
                                    // code和label都可以沒有,全部以name為默認(rèn)
                                    String code = item.getCode() != null ? item.getCode().toString() : item.name();
                                    String label = item.getLabel() != null ? item.getLabel() : item.name();
                                    return new DictItemVO(code, label);
                                }).collect(Collectors.toList());
                        vo.setItems(collect);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        log.error("{}", e.getMessage(), e);
                    }
                    return vo;
                })
                // 這里轉(zhuǎn)map是校驗(yàn)有沒有重復(fù)的type, 如果存在重復(fù)的會報(bào)錯
                .collect(Collectors.toMap(item -> item, item -> item, (a1, a2) -> {throw new E("字典類型:" + a1.getType() + ", 有重復(fù)");}))
                .keySet();
    }
}
// controller層提供外部訪問    
@ApiOperation("系統(tǒng)字典列表查詢")
    @GetMapping(value = "/system")
    public R<Set<DictVO>> querySystemDict() {
        return R.of(systemDictService.getDictSet());
    }
package com.dict.vo;
import lombok.Data;
import java.util.List;
import java.util.Objects;
/**
 * 字典VO
 *
 */
@Data
public class DictVO {
    /**
     * 字典類別名稱
     */
    private String type;
    /**
     * 字典項(xiàng)列表
     */
    private List<DictItemVO> items;
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        DictVO dictVO = (DictVO) o;
        return Objects.equals(type, dictVO.type);
    }
    @Override
    public int hashCode() {
        return Objects.hash(type);
    }
}
package com.dict.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 字典項(xiàng)
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DictItemVO {
    /**
     * 編碼
     */
    private String code;
    /**
     * 標(biāo)簽
     */
    private String label;
}

自定義枚舉類型處理(不建議使用)

這種方式只能支持單個屬性接收值,接收list枚舉,map。set接收枚舉都會報(bào)錯。

由于上邊規(guī)定所有的枚舉都需要實(shí)現(xiàn)Dict接口,下面的反序列化只針對符合條件的處理

處理工具類

package com.common.config;
import com.common.interfaces.Dict;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
 * 枚舉匹配工具類
 */
public class EnumUtil {
    private static final Map<Class<? extends Enum<?>>, Map<Integer, ? extends Enum<? extends Dict>>> CLASS_ENUM_MAP =
            new ConcurrentHashMap<>(16);
    @SuppressWarnings("unchecked")
    public static <E extends Enum<E> & Dict> E match(Class<E> enumClass, Integer type) {
        Map enumMap = CLASS_ENUM_MAP.get(enumClass);
        if (Objects.isNull(enumMap)) {
           // Map<Integer, ? extends Enum<? extends Dict>> unmodifiableMap = Arrays.stream(enumClass.getEnumConstants())
                                             // 這種表達(dá)式寫法會報(bào)錯
            //       .collect(Collectors.toMap(Dict::getCode, v -> v));
              Map<Integer, ? extends Enum<? extends Dict>> unmodifiableMap = Arrays.stream(enumClass.getEnumConstants())
                    .collect(Collectors.toMap(obj -> obj.getCode(), v -> v));
            CLASS_ENUM_MAP.putIfAbsent(enumClass, unmodifiableMap);
            return (E) unmodifiableMap.get(type);
        }
        return (E) enumMap.get(type);
    }
}

自定義的反序列化類

package com.common.config;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
/**
 * 枚舉反序列化器
 */
public class DictEnumDeserializer extends StdDeserializer<Dict> implements ContextualDeserializer {
    public DictEnumDeserializer() {
        super((JavaType) null);
    }
    public DictEnumDeserializer(JavaType valueType) {
        super(valueType);
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        return new DictEnumDeserializer(property.getType());
    }
    @Override
    @SuppressWarnings("all")
    public Dict deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return (Dict) EnumUtil.match((Class) _valueClass, p.getIntValue());
    }
}

注入到spring中

注入方式1

由于項(xiàng)目中還配置有時間的序列化所以就把它們放一起了。建議使用的,把所有的序列化反序列化的都放這。

package com.cnpc.common.config;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.ClassKey;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
/**
 * LocalDateTime配置
 */
@Configuration
// public class LocalDateTimeFormatConfig { 改了一個名字
public class WebCustomerConfig {
    private static final String DEFAULT_DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
    private static final String DEFAULT_TIME_PATTERN = "HH:mm:ss";
// 這里加@Primary,在項(xiàng)目啟動的時候一共會加載12個convert,第7個是MappingJackson2HttpMessageConverter
// 處理json映射對象參數(shù)的類,會采用這個ObjectMapper,默認(rèn)的是 new ObjectMapper();這里自己增加了一些序列化處理的方法
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
// 注冊時間的序列化和反序列化處理
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_PATTERN)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_PATTERN)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_PATTERN)));
        objectMapper.registerModule(javaTimeModule);
        //忽略識別不了的屬性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 注冊枚舉的處理序列化和反序列的方式
        SimpleModule sm = new SimpleModule();
        //自定義查找規(guī)則
        sm.setDeserializers(new SimpleDeserializers() {
            @Override
            public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config,
                                                            BeanDescription beanDesc) throws JsonMappingException {
                JsonDeserializer enumDeserializer = super.findEnumDeserializer(type, config, beanDesc);
                if (enumDeserializer != null) {
                    return enumDeserializer;
                }
                //遍歷枚舉實(shí)現(xiàn)的接口, 查找反序列化器
                for (Class typeInterface : type.getInterfaces()) {
                    // 如果實(shí)現(xiàn)了Dict接口但是沒有code值的就返回null,如果項(xiàng)目比較規(guī)范都有code值,或者沒有code值的,沒有實(shí)現(xiàn)Dict接口,可以去掉下面這個代碼
                   if (typeInterface.equals(Dict.class) ){
                        Dict[] enumConstants = (Dict[]) type.getEnumConstants();
                        if (Objects.isNull(enumConstants[0].getCode())) {
                            return null;
                        }
                    }
                    enumDeserializer = this._classMappings.get(new ClassKey(typeInterface));
                    if (enumDeserializer != null) {
                        return enumDeserializer;
                    }
                }
                return null;
            }
        });
        sm.addDeserializer(Dict.class, new TypeEnumDeserializer());
// 增加枚舉的序列化方式
        sm.addSerializer(Dict.class, new JsonSerializer<Dict>() {
            @Override
            public void serialize(Dict value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                 // 相當(dāng)于在枚舉類上加 @JsonFormat(shape = JsonFormat.Shape.OBJECT);返給頁面的是一個對象code和label都返回
//                gen.writeStartObject();
//                gen.writeNumberField("code", value.getCode());
//                gen.writeStringField("label", value.getLabel());
//                gen.writeEndObject();
                // 相當(dāng)于在枚舉code字段上加 @JsonValue  返回給頁面一個code值
                gen.writeNumber(value.getCode());
            }
            @Override
            public void serializeWithType(Dict value, JsonGenerator gen, SerializerProvider serializers,
                                          TypeSerializer typeSer) throws IOException {
                WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
                serialize(value, gen, serializers);
                typeSer.writeTypeSuffix(gen, typeIdDef);
            }
        });
        objectMapper.registerModule(sm);
        return objectMapper;
    }
}

注入方式2

下面這種注入會影響項(xiàng)目已經(jīng)有配置ObjectMapper的地方,建議都放一起。如果采用下面這種方式,需要自己創(chuàng)建一個MappingJackson2HttpMessageConverter,并將converter添加到list的第一個,如果添加到最后一個,他會先找list中前邊的MappingJackson2HttpMessageConverter,大約是第7個位置,就不會用自己寫的。

package com.common.config;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.ClassKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
 * 裝載枚舉序列化器
 */
@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapperForWebConvert());
        converters.add(0, stringHttpMessageConverter);
        // 將這個MappingJackson2HttpMessageConverter添加到第一個,是為了優(yōu)先找到,否則就有其他MappingJackson2HttpMessageConverter去處理了
        converters.add(0, converter);
    }
// 這個沒有時間的處理配置。
    public ObjectMapper objectMapperForWebConvert() {
        ObjectMapper om = new ObjectMapper();
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        SimpleModule sm = new SimpleModule();
        //自定義查找規(guī)則
        sm.setDeserializers(new SimpleDeserializers() {
            @Override
            public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config,
                                                            BeanDescription beanDesc) throws JsonMappingException {
                JsonDeserializer enumDeserializer = super.findEnumDeserializer(type, config, beanDesc);
                if (enumDeserializer != null) {
                    return enumDeserializer;
                }
                //遍歷枚舉實(shí)現(xiàn)的接口, 查找反序列化器
                for (Class typeInterface : type.getInterfaces()) {
                    // 如果實(shí)現(xiàn)了Dict接口但是沒有code值的就返回null,如果項(xiàng)目比較規(guī)范都有code值,或者沒有code值的,沒有實(shí)現(xiàn)Dict接口,可以去掉下面這個代碼
                    if (typeInterface.equals(Dict.class)){
                        Dict[] enumConstants = (Dict[]) type.getEnumConstants();
                        if (Objects.isNull(enumConstants[0].getCode())) {
                            return null;
                        }
                    }
                                                              // 這里的classKey不要導(dǎo)錯包,必須是com.fasterxml.jackson.databind.type.ClassKey,否則會找不到,導(dǎo)致自定義的反序列化類不起作用
                    enumDeserializer = this._classMappings.get(new ClassKey(typeInterface));
                    if (enumDeserializer != null) {
                        return enumDeserializer;
                    }
                    // 上邊的ClassKey導(dǎo)入包錯誤,找不到臨時加的
//                    if (typeInterface.equals(Dict.class)){
//                        return new DictEnumDeserializer();
//                    }
//                    return new DictEnumDeserializer();
                }
                return null;
            }
        });
        // 添加枚舉反序列化處理器
        sm.addDeserializer(Dict.class, new DictEnumDeserializer());
        sm.addSerializer(Dict.class, new JsonSerializer<Dict>() {
            @Override
            public void serialize(Dict value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                // 相當(dāng)于在枚舉類上加 @JsonFormat(shape = JsonFormat.Shape.OBJECT);返給頁面的是一個對象code和label都返回
//                gen.writeStartObject();
//                gen.writeNumberField("code", value.getCode());
//                gen.writeStringField("label", value.getLabel());
//                gen.writeEndObject();
                // 相當(dāng)于在枚舉code字段上加 @JsonValue  返回給頁面一個code值
                gen.writeNumber(value.getCode());
            }
            @Override
            public void serializeWithType(Dict value, JsonGenerator gen, SerializerProvider serializers,
                                          TypeSerializer typeSer) throws IOException {
                WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
                serialize(value, gen, serializers);
                typeSer.writeTypeSuffix(gen, typeIdDef);
            }
        });
        om.registerModule(sm);
        return om;
    }
}

這樣就可以測試了。沒有實(shí)現(xiàn)Dict接口的枚舉會采用下一個MappingJackson2HttpMessageConverter的new ObjectMapper()去處理,按照默認(rèn)的去處理

斷點(diǎn)測試

第一次賦值

加載自定義的ObjectMapper

現(xiàn)在converter變成了14個了,因?yàn)檫@是注入方式2,自己往里面放了2個,

請求訪問時,斷點(diǎn)

org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

注入方式1斷點(diǎn)測試

啟動過程中會放入2個MappingJackson,會看到第7個下移到第8個了

注入方式2斷點(diǎn)測試

第一次啟動地址是15536

走到自己寫的枚舉處理

改進(jìn)枚舉工具類

改進(jìn)之后,即可已返回給前端枚舉對象,也可以接收對象類型的,也可以接收數(shù)值,也可以接收字符串

package com.common.config;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
/**
 * 枚舉反序列化器
 */
public class DictEnumDeserializer extends StdDeserializer<Dict> implements ContextualDeserializer {
    public DictEnumDeserializer() {
        super((JavaType) null);
    }
    public DictEnumDeserializer(JavaType valueType) {
        super(valueType);
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        return new DictEnumDeserializer(property.getType());
    }
// 自定義的反序列化器這個方法,需要傳JsonParser
    @Override
    @SuppressWarnings("all")
    public Dict deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        return (Dict) EnumUtil.match((Class) _valueClass, p);
    }
}
package com.common.config;
import com.common.interfaces.Dict;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
 * 枚舉匹配工具類
* 改進(jìn)的枚舉工具
 */
public class EnumUtil {
    private static final Map<Class<? extends Enum<?>>, Map<Integer, ? extends Enum<? extends Dict>>> CLASS_ENUM_MAP =
            new ConcurrentHashMap<>(16);
    @SuppressWarnings("unchecked")
    public static <E extends Enum<E> & Dict> E match(Class<E> enumClass, JsonParser jsonParser) throws IOException {
        Integer code = null;
        // 接收數(shù)值型的
        if (jsonParser.currentToken() == JsonToken.VALUE_NUMBER_INT){
            code = jsonParser.getValueAsInt();
        // 接收OBJECT類型的參數(shù)
        } else if (jsonParser.currentToken() == JsonToken.START_OBJECT){
            while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
                String fieldname = jsonParser.getCurrentName();
                if ("code".equals(fieldname)) {
                    jsonParser.nextToken();
                    code = jsonParser.getValueAsInt();
                    break;
                }
            }
        // 接收字符串類型的,需要判斷是純數(shù)字
        }else  if (jsonParser.currentToken() == JsonToken.VALUE_STRING){
            String codestr = jsonParser.getValueAsString();
            if (codestr.matches("^[0-9]*$")) {
                code = Integer.valueOf(codestr);
            }
        }
        if (Objects.isNull(code)){
            throw new RuntimeException("沒有code找不到對應(yīng)的枚舉");
        }
        return getDictEnum(enumClass, code);
    }
    private static <E extends Enum<E> & Dict> E getDictEnum(Class<E> enumClass, Integer type) {
        Map enumMap = CLASS_ENUM_MAP.get(enumClass);
        if (Objects.isNull(enumMap)) {
            Map<Integer, ? extends Enum<? extends Dict>> unmodifiableMap = Arrays.stream(enumClass.getEnumConstants())
                    .collect(Collectors.toMap(obj -> obj.getCode(), v -> v));
            CLASS_ENUM_MAP.putIfAbsent(enumClass, unmodifiableMap);
            E e = (E) unmodifiableMap.get(code);
            if (Objects.isNull(e)){
                throw new RuntimeException("指定類型code找不到對應(yīng)的枚舉code = " + code);
            }
            return e;
        }
        E e = (E) enumMap.get(code);
        if (Objects.isNull(e)){
            throw new RuntimeException("指定類型code找不到對應(yīng)的枚舉code = " + code);
        }
        return e;
    }
// 原來的方法,只能接收數(shù)值code
    /*public static <E extends Enum<E> & Dict> E match(Class<E> enumClass, Integer type) {
        Map enumMap = CLASS_ENUM_MAP.get(enumClass);
        if (Objects.isNull(enumMap)) {
            Map<Integer, ? extends Enum<? extends Dict>> unmodifiableMap = Arrays.stream(enumClass.getEnumConstants())
                    .collect(Collectors.toMap(Dict::getCode, v -> v));
            CLASS_ENUM_MAP.putIfAbsent(enumClass, unmodifiableMap);
            return (E) unmodifiableMap.get(type);
        }
        return (E) enumMap.get(type);
    }*/
}

參考文章

https://developer.aliyun.com/article/979501 

http://events.jianshu.io/p/33e537ea6f10

JsonParser的處理

https://blog.csdn.net/band_mmbx/article/details/126749515  

到此這篇關(guān)于springboot序列化和反序列化器配置方法的文章就介紹到這了,更多相關(guān)springboot序列化和反序列化器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • GC算法實(shí)現(xiàn)篇之并發(fā)標(biāo)記清除

    GC算法實(shí)現(xiàn)篇之并發(fā)標(biāo)記清除

    這篇文章主要為大家介紹了GC算法實(shí)現(xiàn)篇之并發(fā)-標(biāo)記-清除,?CMS垃圾收集器在減少停頓時間上做了很多給力的工作,?大量的并發(fā)線程執(zhí)行的工作并不需要暫停應(yīng)用線程
    2022-01-01
  • Spring bean生命周期配置過程解析

    Spring bean生命周期配置過程解析

    這篇文章主要介紹了Spring bean生命周期配置過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • springboot+gradle 構(gòu)建多模塊項(xiàng)目的步驟

    springboot+gradle 構(gòu)建多模塊項(xiàng)目的步驟

    這篇文章主要介紹了springboot+gradle 構(gòu)建多模塊項(xiàng)目的步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • 解決SpringBoot跨域的三種方式

    解決SpringBoot跨域的三種方式

    前后端分離是目前的趨勢,解決跨域問題也是老生常談的話題了,我們了解一下什么是域和跨域。域:協(xié)議 + 域名 + 端口;三者完全相同則為同域,反之有其一不同均為不同域??缬蛘埱螅寒?dāng)前【發(fā)起請求】的域和【請求指向】的域?qū)儆诓煌驎r,該次請求稱之為跨域請求
    2021-06-06
  • java自定義線程模型處理方法分享

    java自定義線程模型處理方法分享

    本文給大家總結(jié)分享了下個人關(guān)于java處理自定義線程模型的一些經(jīng)驗(yàn)和處理方法,有需要的小伙伴可以參考下
    2016-08-08
  • SpringCloud之LoadBalancer負(fù)載均衡服務(wù)調(diào)用過程

    SpringCloud之LoadBalancer負(fù)載均衡服務(wù)調(diào)用過程

    這篇文章主要介紹了SpringCloud之LoadBalancer負(fù)載均衡服務(wù)調(diào)用過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • IDEA修改java文件后 不用重啟Tomcat服務(wù)便可實(shí)現(xiàn)自動更新

    IDEA修改java文件后 不用重啟Tomcat服務(wù)便可實(shí)現(xiàn)自動更新

    這篇文章主要介紹了IDEA修改java文件后 不用重啟Tomcat服務(wù)便可實(shí)現(xiàn)自動更新,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 解讀靜態(tài)資源訪問static-locations和static-path-pattern

    解讀靜態(tài)資源訪問static-locations和static-path-pattern

    本文主要介紹了Spring Boot中靜態(tài)資源的配置和訪問方式,包括靜態(tài)資源的默認(rèn)前綴、默認(rèn)地址、目錄結(jié)構(gòu)、訪問路徑以及靜態(tài)資源處理器的工作原理,通過配置文件和實(shí)現(xiàn)`WebMvcConfigurer`接口,可以自定義靜態(tài)資源目錄和訪問前綴
    2025-01-01
  • Mybatis分步查詢的實(shí)現(xiàn)示例

    Mybatis分步查詢的實(shí)現(xiàn)示例

    本文主要介紹了Mybatis分步查詢的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • JAVA文件掃描(遞歸)的實(shí)例代碼

    JAVA文件掃描(遞歸)的實(shí)例代碼

    這篇文章主要介紹了JAVA文件掃描(遞歸)的實(shí)例代碼 ,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-06-06

最新評論