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
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(); /** * 序列化:对象=>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; } } |
日期对象的转换
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> 枚举实际值的数据类型 */ 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()); } } |
枚举的转换器
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=>对象 */ @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(""); } } } |
下面才是真正的转换器
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自定义转换器
使用jackson进行json和java bean转换时,可以使用注解自定义转换器进行转换。
@JsonDeserialize注解源码
方法注释中写了,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 ; } |
以日期类型为例
1
2
3
4
5
6
7
8
9
|
@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; } |
自定义转换方法
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)); } } |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/dimandsun/article/details/110484842