springboot jackson使用-自定義參數(shù)轉(zhuǎn)換器
springboot中默認(rèn)使用jackson,且實(shí)現(xiàn)了很多參數(shù)轉(zhuǎn)換器,其中就有EnumToStringConverter和StringToEnumConverterFactory,用于字符串和枚舉的互轉(zhuǎn)。但是是根據(jù)枚舉名稱(chēng)互轉(zhuǎn)。
要實(shí)現(xiàn)的功能
- 空屬性我不希望轉(zhuǎn)成json字符串
- 日期對(duì)象我希望按照指定格式轉(zhuǎn)換
- 我存在多個(gè)枚舉,類(lèi)似public enum ChannelWayEnum { Bluetooth(0, "藍(lán)牙"), NB(1, "NB-IOT"), G4(2, "自建4G"), Ali(3, "ali-4G");},用默認(rèn)轉(zhuǎn)換器無(wú)法轉(zhuǎn)換。需要自定義轉(zhuǎn)換。
思路
- 覆蓋默認(rèn)注入的ObjectMapper,自己實(shí)現(xiàn)objectMapper,可設(shè)置忽略null字段
- 自定義針對(duì)日期對(duì)象的Converter
- 枚舉需要實(shí)現(xiàn)接口IEnum,然后自定義針對(duì)IEnum接口的轉(zhuǎn)換器
關(guān)鍵代碼
注入ObjectMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { return createObjectMapper(); } private ObjectMapper createObjectMapper(){ ObjectMapper objectMapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); /** * 序列化:對(duì)象=>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=>對(duì)象 */ //允許json屬性名不使用雙引號(hào) 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; } } |
日期對(duì)象的轉(zhuǎn)換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@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); } } |
接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/** * 枚舉都要繼承此接口, * @param <V> 枚舉實(shí)際值的數(shù)據(jù)類(lèi)型 */ public interface IEnum<V> { //枚舉實(shí)際值 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()); } } |
枚舉的轉(zhuǎn)換器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/** * json=>對(duì)象 */ @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枚舉轉(zhuǎn)為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(""); } } } |
下面才是真正的轉(zhuǎn)換器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
/** * 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自定義轉(zhuǎn)換器
使用jackson進(jìn)行json和java bean轉(zhuǎn)換時(shí),可以使用注解自定義轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換。
@JsonDeserialize注解源碼
方法注釋中寫(xiě)了,using 方法是作用在method上的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
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 ; } |
以日期類(lèi)型為例
1
2
3
4
5
6
7
8
9
|
@JsonDeserialize (using= DateJsonDeserializer. class ) // Json ==> Bean,需要寫(xiě)到Setter方法上 public void setCreateTime(Date createTime) { this .createTime = createTime; } @JsonSerialize (using= DateJsonSerializer. class ) // Bean ==> Json,需要寫(xiě)到Getter方法上 public Date getCreateTime() { return createTime; } |
自定義轉(zhuǎn)換方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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)); } } |
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/dimandsun/article/details/110484842