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(); // 注意不能直接输出自己,会造成递归* // 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); } }
注意,这种方式不能再次输出当前对象,再次输出会造成*。
测试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继承该类即可。
JsonSerialize重新定义输出的内容格式&JsonDeserializer定义参数转换器&PropertyEditorSupport自定义非JSON数据参数处理器