jackson在springboot中的使用方式-自定义参数转换器
作者:dimandsun
这篇文章主要介绍了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
@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, JsonGenerator gen, 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和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). *<p> * An example annotation would be: *<pre> * @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 <b>Builder</b> * class and NOT on target POJO class: for example @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, fields, constructors), * 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)); } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。