1. 程式人生 > 程式設計 >jackson在springboot中的使用方式-自定義引數轉換器

jackson在springboot中的使用方式-自定義引數轉換器

目錄
  • springboot jackson使用-自定義引數轉換器
    • 要實現的功能
    • 思路
    • 關鍵程式碼
  • Jackson自定義轉換器
    • @onDeserialize註解原始碼
    • 以日期型別為例
    • 自定義轉換方法

springboot jackson使用-自定義引數轉換器

springboot中預設使用jackson,且實現了很多引數轉換器,其中就有EnumToStringConverter和StringToEnumConverterFactory,用於字串和列舉的互轉。但是是根據列舉名稱互轉。

要實現的功能

  • 空屬性我不希望轉成json字串
  • 日期物件我希望按照指定格式轉換
  • 我存在多個列舉,類似public enum ChannelWayEnum { Bluetooth(0,"藍芽"),NB(1,"BAbRZ
    NB-IOT"),G4(2,"自建4G"),Ali(3,"ali-4G");},用預設轉換器無法轉換。需要自定義轉換。

思路

  • 覆蓋預設注入的ObjectMapper,自己實現objectMapper,可設定忽略null欄位
  • 自定義針對日期物件的Converter
  • 列舉需要實現介面IEnum,然後自定義針對IEnum介面的轉換器

關鍵程式碼

注入ObjectMapper

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        return createObjectMapper();
    }
    private ObjectMapper createObjectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        /**
         * 序列化:物件=>jsonString
         */
        simpleModule.addSerializer(WashEnum.class,new WashEnumSerializer());
        simpleModule.addSerializer(IEnum.class,new EnumSerializer());
        simpleModule.addSerializer(Date.class,new DateSerializer());
        simpleModule.addSerializer(Boolean.class,new BooleanSerializer());
        //忽略null欄位
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        /**
         * 反序列化:jsonString=>物件
         */
        //允許json屬性名不使用雙引號
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
        //忽略不存在欄位
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        simpleModule.addDeserializer(String.class,new StringDeserializer());
        simpleModule.addDeserializer(Date.class,new DateDeserializer());
        simpleModule.addDeserializer(WashEnum.class,new WashEnumDeserializer());
        simpleModule.addDeserializer(Enum.class,new EnumDeserializer());//反序列化列舉,
        simpleModule.addDeserializer(Boolean.class,new BooleanDeserializer());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

日期物件的轉換

@JsonComponent
public class DateDeserializer extends JsonDeserializer<Date> implements Converter<String,Date> {
    @Override
    public Date deserialize(JsonParser p,DeserializationContext ctxt) {
        try {
            return convert(p.getText());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public Date convert(String source) {
        if (StringUtil.isBlank(source)) {
            return null;
        }
        return TimeUtil.toDate(TimeUtil.str2Time(source,TimeFormat.DEFAULT));
    }
}
@JsonComponent
public class DateSerializer extends JsonSerializer<Date> implements Converter<Date,String> {
    @Override
    public void serialize(Date value,JsonGenerator gen,SerializerProvider serializers){
        try {
            gen.writeString(convert(value));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public String convert(Date source) {
        return TimeUtil.time2Str(TimeUtil.date2Time(source),TimeFormat.DEFAULT);
    }
}

介面

/**
 * 列舉都要繼承此介面,
 * @param <V> 列舉實際值的資料型別
 */
public interface IEnum<V> {
    //列舉實際值
    V getValue();
    static<T extends IEnum> T getBean(String value,Class<T> tClass){
        if (StringUtil.isBlank(value)){
            return null;
        }
        for (T enumObj : tClass.getEnumConstants()) {
            if (value.equals(enumObj.getValue().toString())) {
                return enumObj;
            }
        }
        return null;
    }
    default String getStr(){
        return String.valueOf(getValue());
    }
}

列舉的轉換器

/**
 * json=>物件
 */
@JsonComponent
public class EnumDeserializer<T extends IEnum> extends JsonDeserializer<T> implements ContextualDeserializer{
    private Class<T> targetClass = null;
    public EnumDeserializer() {
    }
    public EnumDeserializer(Class<T> targetClass) {
        this.targetClass = targetClass;
    }
    @Override
    public T deserialize(JsonParser p,DeserializationContext ctxt) {
//        if(targetClass!=null&&IEnum.class.isAssignableFrom(targetClass)){
            try {
                return IEnum.getBean(p.getText(),targetClass);
            } catch (IOException e) {
                e.printStackTrace();
            }
//        }
        return null;
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,BeanProperty property) {
        Class<T> targetClass = (Class<T>) ctxt.getContextualType().getRawClass();
        return new EnumDeserializer(targetClass);
    }
}
/**
 * 序列化,將enum列舉轉為json
 * @author chenzy
 * @since 2019.12.19
 */
@JsonComponent
public class EnumSerializer<T extends IEnum> extends JsonSerializer<T> {
    @Override
    public void serialize(T value,SerializerProvider serializers) throws IOException {
        Optional<T> data = Optional.of(value);
        if (data.isPresent()) {//非空
            gen.writeObject(data.get().getValue());
        } else {
//            gen.writeString("");
        }
    }
}

下面才是真正的轉換器

/**
 * IEnum=>str
 */
@Component
public class Enum2StrConverter<T extends IEnum<?>> implements ConditionalConverter,Converter<T,String>{
    private final ConversionService conversionService;
    protected Enum2StrConverter(ConversionService conversionService) {
        this.conversionService = conversionService;
    }
    @Override
    public String convert(T source) {
        return source.getStr();
    }
    @Override
    public boolean matches(TypeDescriptor sourceType,TypeDescriptor targetType) {
        for (Class<?> interfaceType : ClassUtils.getAllInterfacesForClassAsSet(sourceType.getType())) {
            if (this.conversionService.canConvert(TypeDescriptor.valueOf(interfaceType),targetType)) {
                return false;
            }
        }
        return true;
    }
}
/**
 * str=>IEnum
 */
@Component
public class Str2EnumConverte implements ConverterFactory<String,IEnum> {
    @Override
    public <T extends IEnum> Converter<String,T> getConverter(Class<T> targetType) {
        return new Str2Enum(targetType);
    }
    private static class Str2Enum<T extends IEnum> implements Converter<String,T> {
        private final Class<T> enumType;
        public Str2Enum(Class<T> enumType) {
            this.enumType = enumType;
        }
        @Override
        public T convert(String source) {
            if (StringUtil.isBlank(source)) {
                return null;
            }
            return IEnum.getBean(source,enumType);
        }
    }
}
/**
 * @author chenzy
 * @since 2020-12-02
 */
@Configuration
public class JacksonConfig  implements WebMvcConfigurer {
    @Autowired private Str2EnumConverte str2EnumConverte;
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(str2EnumConverte);
    }
    @Bean
    public ObjectMapper objectMapper() {
        return JsonUtil.getObjectMapper();
    }
}

Jackson自定義轉換器

使用jackson進行json和 bean轉換時,可以使用註解自定義轉換器進行轉換。

@JsonDeserialize註解原始碼

方法註釋中寫了,using 方法是作用在method上的。

 
package com.fasterxml.jackson.databind.annotation; 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; 
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.util.Converter;
 
/**
 * Annotation use for configuring deserialization aspects,by attaching
 * to "setter" methods or fields,or to value classes.
 * When annotating value classes,configuration is used for instances
 * of the value class but can be overridden by more specific annotations
 * (ones that attach to methods or fields).
 *<p>
 * An example annotation would be:
 *<pre>
 *  &#64;JsonDeserialize(using=MySerializer.class,*    as=MyHashMap.class,*    keyAs=MyHashKey.class,*    contentAs=MyHashValue.class
 *  )
 *</pre>
 *<p>
 * Something to note on usage:
 *<ul>
 * <li>All other annotations regarding behavior during building should be on Builder
 *    class and NOT on target POJO class: for example &#64;JsonIgnoreProperties should be on
 *    Builder to prevent "unknown property" errors.
 *  </li>
 * <li>Similarly configuration overrides (see {@link com.fasterxml.jackson.databind.ObjectMapper#configOverride})
 *    should be targeted at Builder class,not target POJO class.
 *  </li>
 * </ul>
 *
 */
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD,ElementType.FIELD,ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonDeserialize
{
    // // // Annotations for explicitly specifying deserialize/builder
 
    /**
     * Deserializer class to use for deserializing associated value.
     * Depending on what is annotated,* value is either an instance of annotated class (used globablly
     * anywhere where class deserializer is needed); or only used for
     * deserializing property access via a setter method.
     */
    @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
    public Class<? extends JsonDeserializer> using()
        default JsonDeserializer.None.class;
 
    /**
     * Deserializer class to use for deserializing contents (elements
     * of a Collection/array,values of Maps) of annotated property.
     * Can only be used on instances (methods,fields,constructors),* and not value classes themselves.
     */
    @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
    public Class<? extends JsonDeserializer> contentUsing()
        default JsonDeserializer.None.class;
 
    /**
     * Deserializer class to use for deserializing Map keys
     * of annotated property.
     * Can only be used on instances (methods,* and not value classes themselves.
     */
    public Class<? extends KeyDeserializer> keyUsing()
        default KeyDeserializer.None.class;
 
    /**
     * Annotation for specifying if an external Builder class is to
     * be used for building up deserialized instances of annotated
     * class. If so,an instance of referenced class is first constructed
     * (possibly using a Creator method; or if none defined,using default
     * constructor),and its "with-methods" are used for populating fields;
     * and finally "build-method" is invoked to complete deserialization.
     */
    public Class<?> builder() default Void.class;
 
    // // // Annotations for specifying intermediate Converters (2.2+)
    
    /**
     * Which helper object (if any) is to be used to convert from Jackson-bound
     * intermediate type (source type of converter) into actual property type
     * (which must be same as result type of converter). This is often used
     * for two-step deserialization; Jackson binds data into suitable intermediate
     * type (like Tree representation),and converter then builds actual property
     * type.
     *
     * @since 2.2
     */
    @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
    public Class<? extends Converter> converter() default Converter.None.class;
 
    /**
     * Similar to {@link #converter},but used for values of structures types
     * (List,arrays,Maps).
     *
     * @since 2.2
     */
    @SuppressWarnings("rawtypes") // to work around JDK8 bug wrt Class-valued annotation properties
    public Class<? extends Converter> contentConverter() default Converter.None.class;
        
    // // // Annotations for explicitly specifying deserialization type
    // // // (which is used for choosing deserializer,if not explicitly
    // // // specified
 
    /**
     * Concrete type to deserialize values as,instead of type otherwise
     * declared. Must be a subtype of declared type; otherwise an
     * exception may be thrown by deserializer.
     *<p>
     * Bogus type {@link Void} can be used to indicate that declared
     * type is used as is (i.e. this annotation property has no setting);
     * this since annotation properties are not allowed to have null value.
     *<p>
     * Note: if {@link #using} is also used it has precedence
     * (since it directly specified
     * deserializer,whereas this would only be used to locate the
     * deserializer)
     * and value of this annotation property is ignored.
     */
    public Class<?> as() default Void.class;
 
    /**
     * Concrete type to deserialize keys of {@link java.util.Map} as,* instead of type otherwise declared.
     * Must be a subtype of declared type; otherwise an exception may be
     * thrown by deserializer.
     */
    public Class<?> keyAs() default Void.class;
 
    /**
     * Concrete type to deserialize content (elements
     * of a Collection/array,values of Maps) values as,* instead of type otherwise declared.
     * Must be a subtype of declared type; otherwise an exception may be
     * thrown by deserializer.
     */
    public Class<?> contentAs() default Void.class;
}
 
客棧

以日期型別為例

@JsonDeserialize(using= DateJsonDeserializer.class) // Json ==> Bean,需要寫到Setter方法上
public void setCreateTime(Date createTime) {
    this.createTime = createTime;
}
 
@JsonSerialize(using= DateJsonSerializer.class) // Bean ==> Json,需要寫到Getter方法上
public Date getCreateTime() {
    return createTime;
}

自定義轉換方法

public class DateJsonDeserializer extends JsonDeserializer<Date> {
    public static final SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public Date deserialize(com.fasterxml.jackson.core.JsonParser jsonParser,DeserializationContext deserializationContext) throws IOException,com.fasterxml.jackson.core.JsonProcessingException {
 
        try {
            if(jsonParser!=null&&StringUtils.isNotEmpty(jsonParser.getText())){
                return format.parse(jsonParser.getText());
            }else {
                return null;
            }
 
        } catch(Exception e) {
            System.out.println(e.getMessage());
            throw new RuntimeException(e);
        }
    } 
} 
 
public class DateJsonSerializer extends JsonSerializer<Date> {
    public static final SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
    @Override
    public void serialize(Date date,JsonGenerator jsonGenerator,SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(format.format(date));
    } 
}

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。