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繼承該類即可。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM