千家信息网

jackson在springboot中如何实现自定义参数转换器

发表于:2025-02-21 作者:千家信息网编辑
千家信息网最后更新 2025年02月21日,小编给大家分享一下jackson在springboot中如何实现自定义参数转换器,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!springboot jackson使用-自定义参数转换
千家信息网最后更新 2025年02月21日jackson在springboot中如何实现自定义参数转换器

小编给大家分享一下jackson在springboot中如何实现自定义参数转换器,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

springboot jackson使用-自定义参数转换器

springboot中默认使用jackson,且实现了很多参数转换器,其中就有EnumToStringConverter和StringToEnumConverterFactory,用于字符串和枚举的互转。但是是根据枚举名称互转。

要实现的功能

  • 空属性我不希望转成json字符串

  • 日期对象我希望按照指定格式转换

  • 我存在多个枚举,类似public enum ChannelWayEnum { Bluetooth(0, "蓝牙"), NB(1, "NB-IOT"), G4(2, "自建4G"), Ali(3, "ali-4G");},用默认转换器无法转换。需要自定义转换。

思路

  • 覆盖默认注入的ObjectMapper,自己实现objectMapper,可设置忽略null字段

  • 自定义针对日期对象的Converter

  • 枚举需要实现接口IEnum,然后自定义针对IEnum接口的转换器

关键代码

注入ObjectMapper

@Configurationpublic 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;    }}

日期对象的转换

@JsonComponentpublic class DateDeserializer extends JsonDeserializer implements Converter {    @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));    }}@JsonComponentpublic class DateSerializer extends JsonSerializer implements Converter {    @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  枚举实际值的数据类型 */public interface IEnum {    //枚举实际值    V getValue();    static T getBean(String value,Class 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=>对象 */@JsonComponentpublic class EnumDeserializer extends JsonDeserializer implements ContextualDeserializer{    private Class targetClass = null;    public EnumDeserializer() {    }    public EnumDeserializer(Class 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 targetClass = (Class) ctxt.getContextualType().getRawClass();        return new EnumDeserializer(targetClass);    }}/** * 序列化,将enum枚举转为json * @author chenzy * @since 2019.12.19 */@JsonComponentpublic class EnumSerializer extends JsonSerializer {    @Override    public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {        Optional data = Optional.of(value);        if (data.isPresent()) {//非空            gen.writeObject(data.get().getValue());        } else {//            gen.writeString("");        }    }}

下面才是真正的转换器

/** * IEnum=>str */@Componentpublic class Enum2StrConverter> implements ConditionalConverter,Converter{    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 */@Componentpublic class Str2EnumConverte implements ConverterFactory {    @Override    public  Converter getConverter(Class targetType) {        return new Str2Enum(targetType);    }    private static class Str2Enum implements Converter {        private final Class enumType;        public Str2Enum(Class 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 */@Configurationpublic 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和java 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). *

* An example annotation would be: *

 *  @JsonDeserialize(using=MySerializer.class, *    as=MyHashMap.class, *    keyAs=MyHashKey.class, *    contentAs=MyHashValue.class *  ) *
*

* Something to note on usage: *

    *
  • All other annotations regarding behavior during building should be on Builder * class and NOT on target POJO class: for example @JsonIgnoreProperties should be on * Builder to prevent "unknown property" errors. *
  • *
  • Similarly configuration overrides (see {@link com.fasterxml.jackson.databind.ObjectMapper#configOverride}) * should be targeted at Builder class, not target POJO class. *
  • *
* */@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@com.fasterxml.jackson.annotation.JacksonAnnotationpublic @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 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 contentUsing() default JsonDeserializer.None.class; /** * Deserializer class to use for deserializing Map keys * of annotated property. * Can only be used on instances (methods, fields, constructors), * and not value classes themselves. */ public Class 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 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 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. *

* 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. *

* 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 {    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 {    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));    } }

看完了这篇文章,相信你对"jackson在springboot中如何实现自定义参数转换器"有了一定的了解,如果想了解更多相关知识,欢迎关注行业资讯频道,感谢各位的阅读!

0