• 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继承该类即可。

    【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
  • 相关阅读:
    mysql 关联关系
    Powershell
    判断Server Manager里面的Role是否已经安排
    Powershell 获取文件版本信息
    PowerShell---Operators 介绍
    C#代码覆盖率 -vsinstr和OpenCover
    敏捷测试介绍
    c#中abstract、override、new、virtual、sealed使用
    装箱和拆箱
    Code Review
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/14304364.html
Copyright © 2020-2023  润新知