jackson在springboot中的使用方式-自定義引數轉換器
阿新 • • 發佈:2021-10-12
目錄
- springboot jackson使用-自定義引數轉換器
- 要實現的功能
- 思路
- 關鍵程式碼
- Jackson自定義轉換器
- @onDeserialize註解原始碼
- 以日期型別為例
- 自定義轉換方法
springboot jackson使用-自定義引數轉換器
springboot中預設使用jackson,且實現了很多引數轉換器,其中就有EnumToStringConverter和StringToEnumConverterFactory,用於字串和列舉的互轉。但是是根據列舉名稱互轉。
要實現的功能
- 空屬性我不希望轉成json字串
- 日期物件我希望按照指定格式轉換
- 我存在多個列舉,類似public enum ChannelWayEnum { Bluetooth(0,"藍芽"),NB(1,"BAbRZ
思路
- 覆蓋預設注入的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> * @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 @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)); } }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。