jackson在springboot中的使用方式-自定义参数转换器

目录

  • springboot jackson使用-自定义参数转换器
    • 要实现的功能
    • 思路
    • 关键代码
  • Jackson自定义转换器
    • @JsonDeserialize注解源码
    • 以日期类型为例
    • 自定义转换方法

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 {@Beanpublic 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 {@Overridepublic Date deserialize(JsonParser p, DeserializationContext ctxt) {try {return convert(p.getText()); } catch (IOException e) {e.printStackTrace(); }return null; }@Overridepublic 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 {@Overridepublic void serialize(Date value, JsonGenerator gen, SerializerProvider serializers){try {gen.writeString(convert(value)); } catch (IOException e) {e.printStackTrace(); }}@Overridepublic 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; }@Overridepublic 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; }@Overridepublic 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 {@Overridepublic void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {Optional data = https://www.it610.com/article/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; }@Overridepublic String convert(T source) {return source.getStr(); }@Overridepublic 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 {@Overridepublic 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; }@Overridepublic T convert(String source) {if (StringUtil.isBlank(source)) {return null; }return IEnum.getBean(source,enumType); }}}/** * @author chenzy * @since 2020-12-02 */@Configurationpublic class JacksonConfigimplements WebMvcConfigurer {@Autowired private Str2EnumConverte str2EnumConverte; @Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverterFactory(str2EnumConverte); }@Beanpublic 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 propertiespublic 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 propertiespublic 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 propertiespublic 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 propertiespublic 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"); @Overridepublic 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"); @Overridepublic void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {jsonGenerator.writeString(format.format(date)); } }

【jackson在springboot中的使用方式-自定义参数转换器】以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

    推荐阅读