服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - jackson在springboot中的使用方式-自定义参数转换器

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

2022-02-15 15:59dimandsun Java教程

这篇文章主要介绍了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

?
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>
 *  &#64;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 &#64;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

延伸 · 阅读

精彩推荐
  • Java教程java compareTo和compare方法比较详解

    java compareTo和compare方法比较详解

    这篇文章主要介绍了java compareTo和compare方法比较详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    华电岳黎明10032021-12-22
  • Java教程Java探索之Feign入门使用详解

    Java探索之Feign入门使用详解

    这篇文章主要介绍了Java探索之Feign入门使用详解,关于feign,我也是第一次遇到,于是在网上搜集了相关文章,这篇比较详细,介绍了其简介,选择feign的原...

    一杯甜酒4272021-01-27
  • Java教程javaweb文件打包批量下载代码

    javaweb文件打包批量下载代码

    这篇文章主要为大家详细介绍了javaweb文件打包批量下载代码,批量下载未批改作业,感兴趣的小伙伴们可以参考一下 ...

    acmjk4882020-05-22
  • Java教程SpringMVC接收页面表单参数

    SpringMVC接收页面表单参数

    本篇文章主要介绍了SpringMVC接收页面表单参数的相关知识,具有很好的参考价值。下面跟着小编一起来看下吧...

    碧波寒3352020-09-22
  • Java教程Java 关键字static详解及实例代码

    Java 关键字static详解及实例代码

    这篇文章主要介绍了Java 关键字static详解及实例代码的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-18
  • Java教程使用maven运行Java Main的三种方法解析

    使用maven运行Java Main的三种方法解析

    这篇文章主要介绍了使用maven运行Java Main的三种方式的相关内容,具有一定参考价值,需要的朋友可以了解下。...

    JavaDynamic7972021-01-18
  • Java教程java实现2048小游戏

    java实现2048小游戏

    这篇文章主要为大家详细介绍了java实现2048小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    小样54119772021-08-04
  • Java教程详解IDEA自定义注释模板(javadoc)

    详解IDEA自定义注释模板(javadoc)

    这篇文章主要介绍了详解IDEA自定义注释模板(javadoc),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    Jaynnay4922021-04-21