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

目录
  • springboot jackson使用-自定义参数转换器
    • 要实现的功能
    • 思路
    • 关键代码
  • Jackson自定义转换器
    • @JsonDeserialize注解源码
    • 以日期类型为例
    • 自定义转换方法

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));
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 使用jackson实现对象json之间的相互转换(spring boot)

    目录 首先,在pom.xml里弄好依赖 用来获取天气预报接口的数据 返回的json字符串就像下面这个样子 我拆成了下面两个对象 开始书写工具类,方便以后调用~ 封装完成,写测试类 之前的json转对象,对象转json.总是比较繁琐,不够简洁.自从接触到jackson之后,发现原来对象和json转换可以这么简单.拿一个天气预报的小例子来说明一下~如下图.[若是有小误,还望指正] 不说,直接上码~ 首先,在pom.xml里弄好依赖 具体依赖需要上网去查找,咱用的是下面这个. <!-- 对象转换成js

  • 详解json在SpringBoot中的格式转换

    @RestController自动返回json /** * json 三种实现方法 * 1 @RestController自动返回json */ @GetMapping("/json") public Student getjson() { Student student = new Student("bennyrhys",158 ); return student; } @ResponseBody+@Controller 组合返回json //@RestContr

  • Spring boot中Jackson的操作指南

    前言 有一段时间没写博客了,虽然是菜鸟一枚但毕竟总要有东西记录学习的,我相信有志者事竟成.今天在工作中使用Jackson转换了一个javabean,传到测试服上之后发现日期少了一天,使用的是@JsonFormat注解. Spring-Boot是基于Spring框架的,它并不是对Spring框架的功能增强,而是对Spring的一种快速构建的方式. 好了,下面话不多说了,来一起看看详细的介绍吧 这里写了一个简单的小demo记录一下: 表数据: 实体类属性: controller层就省略掉了,就是调用

  • SpringBoot使用自定义json解析器的使用方法

    Spring-Boot是基于Spring框架的,它并不是对Spring框架的功能增强,而是对Spring的一种快速构建的方式. Spring-boot应用程序提供了默认的json转换器,为Jackson.示例: pom.xml中dependency配置: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

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

    目录 springboot jackson使用-自定义参数转换器 要实现的功能 思路 关键代码 Jackson自定义转换器 @JsonDeserialize注解源码 以日期类型为例 自定义转换方法 springboot jackson使用-自定义参数转换器 springboot中默认使用jackson,且实现了很多参数转换器,其中就有EnumToStringConverter和StringToEnumConverterFactory,用于字符串和枚举的互转.但是是根据枚举名称互转. 要实现的功能

  • SpringBoot中@Autowired生效方式详解

    目录 前言 正文 注册AutowiredProcessor的BeanDefinition 实例化AutowiredProcessor 创建bean时进行注入 后记 前言 @Component public class SimpleBean3 { @Autowired private SimpleBean simpleBean; } @Autowired修饰的字段会被容器自动注入.那么Spring Boot中使如何实现这一功能呢? AutowiredAnnotationBeanPostProces

  • springboot中不能获取post请求参数的解决方法

    问题描述 最近在做微信小程序,用的spring boot做后端,突然发现客户端发送post请求的时候服务端接收不到参数.问题简化之后如下: 微信小程序端: 在页面放一个按钮进行测试 <!--index.wxml--> <view class="container"> <button catchtap='testpost'>点击进行测试</button> </view> 绑定一个函数发送post请求 //index.js //获

  • SpringBoot项目实用功能之实现自定义参数解析器

    核心点 1.实现接口 org.springframework.web.method.support.HandlerMethodArgumentResolver supportsParameter 方法根据当前方法参数决定是否需要应用当前这个参数解析器 resolveArgument 执行具体的解析过程 2.将自实现的参数解析器类 添加到Spring容器中 3.实现 org.springframework.web.servlet.config.annotation.WebMvcConfigurer

  • 关于springboot中对sqlSessionFactoryBean的自定义

    目录 springboot sqlSessionFactoryBean自定义 代码如下 以上配置也可以通过properties文件配置 springboot启动报找不到sqlSessionFactory springboot sqlSessionFactoryBean自定义 1.新建一个配置类,加上configuration注解 2.定制化SqlSessionFactoryBean,然后交给容器管理 代码如下 @Configuration public class MybatisConfig {

  • springboot中PostMapping正常接收json参数后返回404问题

    目录 PostMapping接收json参数后返回404 问题描述 解决 @PostMapping注解解析 PostMapping接收json参数后返回404 问题描述 js中传递json数据给后端,后端可以正常接收参数,但返回404. js                 function rootConfirm(ids, types) {                     $.tool.confirm("确定结束" + options.modalName + "?&

  • SpringBoot中自定义参数绑定步骤详解

    正常情况下,前端传递来的参数都能直接被SpringMVC接收,但是也会遇到一些特殊情况,比如Date对象,当我的前端传来的一个日期时,就需要服务端自定义参数绑定,将前端的日期进行转换.自定义参数绑定也很简单,分两个步骤: 1.自定义参数转换器 自定义参数转换器实现Converter接口,如下: public class DateConverter implements Converter<String,Date> { private SimpleDateFormat simpleDateFor

  • 解决springboot中配置过滤器以及可能出现的问题

    在springboot添加过滤器有两种方式: 1.通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中需要增加cookie可能会存在写不进前端情况) 2.通过注解@WebFilter的方式 通过创建FilterRegistrationBean的方式创建多个filter以及设置执行顺序: 1.创建两个实现Filter接口的类TestFilter1 .TestFilter2 package com.aoxun.c

  • SpringBoot 中使用 Validation 校验参数的方法详解

    目录 1. Validation 介绍 1.1 Validation 注解 1.2 @valid 和 @validated的区别 2. SpringBoot 中使用 Validator 校验参数 2.1 依赖引入 2.2 标注校验实体类 2.3 开启参数校验 2.3.1 简单参数校验 2.3.2 JavaBean 校验 2.4 捕捉参数校验异常 项目中写逻辑时,为保证程序的健壮性,需要对各种参数进行判断,这就导致业务代码不只健壮,还十分臃肿.其实 SpringBoot 中已经提供了 Valida

  • 在SpringBoot 中从application.yml中获取自定义常量方式

    要注意的地方是 application.yml 中不能用驼峰式写法(systemParams)要改成system-params 方法一: 引入依赖: <!-- 支持 @ConfigurationProperties 注解 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</a

随机推荐