JsonSerialize重新定义输出的内容格式&JsonDeserializer定义参数转换器&PropertyEditorSupport自定义非JSON数据参数处理器

1.JsonSerialize重新定义输出的内容格式

  有时候需要重新定义输出的内容格式,或者在输出的JSON数据中增加一个属性。比如一个场景,日期类型的字段,通常在返回的JSON数据中会增加一个日期的字符串格式,比如原字段叫createTime,会增加一个createTimeString 字段。第一种做法是VO中增加getCreateTimeString方法,第二种就是用@JsonSerialize 注解。

比如:

package com.xm.ggn.test;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.util.Date;

@Data
public class TestBean {

    private String name;

    @JsonSerialize(using = DateJsonSerizlizer.class)
    private Date createTime;
}

序列化代码如下:

package com.xm.ggn.test;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.io.IOException;
import java.util.Date;

/**
 * 自定义输出到responseWriter的内容
 *
 * @author: 乔利强
 * @date: 2021/1/18 20:28
 * @see TestBean#createTime
 */
public class DateJsonSerizlizer extends JsonSerializer<Date> {

    @Override
    public void serialize(Date value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        // 先将本字段写出去
        String fieldName = jsonGenerator.getOutputContext().getCurrentName();
        jsonGenerator.writeObject(value);

        // 多输出一个字段(名称为字段名+String)
        String fieldStrName = fieldName + "String";
        String dateStr = null;
        if (value != null) {
            dateStr = DateFormatUtils.format(value, "yyyy-MM-dd HH:mm:ss");
        }
        jsonGenerator.writeStringField(fieldStrName, dateStr);
    }
}

测试Controller:

    /**
     * 测试@JsonSerialize 的用法,自定义输出的字段内容
     *
     * @author 乔利强
     * @date 2021/1/18 20:34
     * @return: com.xm.ggn.test.TestBean
     */
    @GetMapping("/findTestBean")
    public TestBean findTestBean() {
        TestBean testBean = new TestBean();
        testBean.setCreateTime(new Date());
        testBean.setName("testBean");
        return testBean;
    }

结果:

$ curl http://localhost:8088/findTestBean
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   142    0   142    0     0   6761      0 --:--:-- --:--:-- --:--:--  7100{"success":true,"data":{"name":"testBean","createTime":1611116695656,"createTimeString":"2021-01-20 12:24:55"},"msg":"鎴愬姛","errorCode":"0"}

补充:这种方式是单独的配置,相当于每个需要处理的都打注解,代码侵入性也比较强,可以增加全局配置

package com.xm.ggn.test;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.boot.jackson.JsonComponent;

import java.io.IOException;
import java.util.Date;

/**
 * 自定义输出到responseWriter的内容
 *
 * @author: 乔利强
 * @date: 2021/1/18 20:28
 * @see TestBean#createTime
 */
@JsonComponent
public class DateJsonSerizlizer extends JsonSerializer<Date> {

    @Override
    public void serialize(Date value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        // 先将本字段写出去
        String fieldName = jsonGenerator.getOutputContext().getCurrentName();
        // 注意不能直接输出自己,会造成递归StackOverFlow
//        jsonGenerator.writeObject(value);
        jsonGenerator.writeString(DateFormatUtils.format(value, "yyyy-MM-dd HH:mm:ss"));

        // 多输出一个字段(名称为字段名+String)
        String fieldStrName = fieldName + "String";
        String dateStr = null;
        if (value != null) {
            dateStr = DateFormatUtils.format(value, "yyyy-MM-dd HH-mm-ss");
        }
        jsonGenerator.writeStringField(fieldStrName, dateStr);
    }
}

  注意,这种方式不能再次输出当前对象,再次输出会造成StackOverFlow。

测试Controller同上面,测试结果:

$ curl http://localhost:8088/findTestBean
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   150    0   150    0     0    270      0 --:--:-- --:--:-- --:--:--   271{"success":true,"data":{"name":"testBean","createTime":"2021-01-20 17:57:32","createTimeString":"2021-01-20 17-57-32"},"msg":"鎴愬姛","errorCode":"0"}

 2.JsonDeserializer 转换接受到的参数到bean

   同样使用方式有两种,第一种是针对单个属性用@JsonDeserialize 注解,第二种就是全局的。这种只对@RequestBody 注解有效,对普通form表单提交的参数无效。

1.第一种:针对单个属性

 反序列类:

package com.xm.ggn.test;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.io.IOException;
import java.util.Date;

/**
 * JSON字符串转日期
 *
 */
@Slf4j
class DateJsonDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        Date date = null;
        String text = jsonParser.getText();
        try {
            if (StringUtils.isNotBlank(text)) {
                date = DateUtils.parseDate(text, "yyyy-MM-dd HH-mm-ss", "yyyy-MM-dd");
                log.info("text: {}, date: {}", text, date);
            }
        } catch (Exception e) {
            // ignore
        }
        return date;
    }

    @Override
    public Class<?> handledType() {
        return Date.class;
    }
}

接收参数的bean:

package com.xm.ggn.test;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Data;

import java.util.Date;

@Data
public class TestBean {

    private String name;

    //    @JsonSerialize(using = DateJsonSerizlizer.class)
    @JsonDeserialize(using = DateJsonDeserializer.class)
    private Date createTime;
}

测试Controller:

    @PostMapping("/findTestBean2")
    public TestBean findTestBean2(@RequestBody TestBean bean) {
        return bean;
    }

 测试结果:

$ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{"createTime":"1995-02-03 22-22-22"}' 'http://localhost:8088/findTestBean2'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   180    0   144  100    36   6545   1636 --:--:-- --:--:-- --:--:--  8571{"success":true,"data":{"name":null,"createTime":"1995-02-03 22:22:22","createTimeString":"1995-02-03 22-22-22"},"msg":"鎴愬姛","errorCode":"0"}

2. 全局设置

全局也是用@JsonComponent 注解

package com.xm.ggn.test;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.boot.jackson.JsonComponent;

import java.io.IOException;
import java.util.Date;

/**
 * JSON字符串转日期
 */
@Slf4j
@JsonComponent
class DateJsonDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        Date date = null;
        String text = jsonParser.getText();
        try {
            if (StringUtils.isNotBlank(text)) {
                date = DateUtils.parseDate(text, "yyyy-MM-dd HH-mm-ss", "yyyy-MM-dd");
                log.info("text: {}, date: {}", text, date);
            }
        } catch (Exception e) {
            // ignore
        }
        return date;
    }

    @Override
    public Class<?> handledType() {
        return Date.class;
    }
}

bean如下:

package com.xm.ggn.test;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Data;

import java.util.Date;

@Data
public class TestBean {

    private String name;

    private Date createTime;
}

 测试Controller同上,测试结果如下:

$ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{"createTime":"1995-02-03 22-22-22"}' 'http://localhost:8088/findTestBean2'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   180    0   144  100    36    218     54 --:--:-- --:--:-- --:--:--   273{"success":true,"data":{"name":null,"createTime":"1995-02-03 22:22:22","createTimeString":"1995-02-03 22-22-22"},"msg":"鎴愬姛","errorCode":"0"}

补充:还有另一种全局注册的方式,如下: 

package com.xm.ggn.test;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;

/**
 * 配置接收JSON数据日期类型转换器
 */
@Configuration
public class ConverterConfig {

    @Bean
    public Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean(DateJsonDeserializer dateJacksonConverter) {
        Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = new Jackson2ObjectMapperFactoryBean();
        jackson2ObjectMapperFactoryBean.setDeserializers(new DateJsonDeserializer());
        jackson2ObjectMapperFactoryBean.setSerializers(new DateJsonSerizlizer());
        return jackson2ObjectMapperFactoryBean;
    }
}

3. 针对GET参数等非JSON数据转换器的使用 

  PropertyEditorSupport  结合WebDataBinder 类的使用。 Spring也内置了许多PropertyEditorSupport  ,比如CustomDateEditor、 CustomMapEditor 等。这种针对@RequestBody 的参数无效。

1. 转换器类:

package com.xm.ggn.test;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.util.StringUtils;

import java.beans.PropertyEditorSupport;
import java.util.Date;

/***/
@Slf4j
public class DateEditor extends PropertyEditorSupport {

    private boolean isAllowEmpty = true;

    public DateEditor() {
    }

    public DateEditor(boolean isAllowEmpty) {
        this.isAllowEmpty = isAllowEmpty;
    }

    @Override
    @SneakyThrows
    public void setAsText(String text) throws IllegalArgumentException {
        Date date = null;
        if (StringUtils.hasText(text)) {
            date = DateUtils.parseDate(text, "yyyy-MM-dd HH-mm-ss", "yyyy-MM-dd");
            log.info("text: {}, date: {}", text, date);
        } else {
            if (!isAllowEmpty) {
                throw new IllegalArgumentException("日期不允许为空");
            }
        }
        setValue(date);
    }

    public boolean isAllowEmpty() {
        return isAllowEmpty;
    }

    public void setAllowEmpty(boolean isAllowEmpty) {
        this.isAllowEmpty = isAllowEmpty;
    }
}

2. Controller用@InitBinder 绑定 以及测试:

    @GetMapping("/findTestBean3")
    public TestBean findTestBean3(TestBean bean) {
        return bean;
    }

    @PostMapping("/findTestBean4")
    public TestBean findTestBean4(TestBean bean) {
        return bean;
    }

    /**
     * 自定义参数转换器
     *
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Date.class, new DateEditor());
//     也可以使用spring内置的一些转换器。
//        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
//        binder.registerCustomEditor(Date.class, "createTime", new CustomDateEditor(simpleDateFormat, true));
    }

3.测试:

$ curl http://localhost:8088/findTestBean3?createTime=2022-02-22+22-22-22
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   144    0   144    0     0   5760      0 --:--:-- --:--:-- --:--:--  6000{"success":true,"data":{"name":null,"createTime":"2022-02-22 22:22:22","createTimeString":"2022-02-22 22-22-22"},"msg":"鎴愬姛","errorCode":"0"}

$ curl -X POST http://localhost:8088/findTestBean4?createTime=2022-02-22+22-22-22
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   144    0   144    0     0  10285      0 --:--:-- --:--:-- --:--:-- 11076{"success":true,"data":{"name":null,"createTime":"2022-02-22 22:22:22","createTimeString":"2022-02-22 22-22-22"},"msg":"鎴愬姛","errorCode":"0"}

注意: 也可以定义一个BaseController, 里面定义initBinder方法,其他Controller继承该类即可。

【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
原文地址:https://www.cnblogs.com/qlqwjy/p/14304364.html