数据交换格式就是服务器端与客户端之间进行数据传输与数据交换的格式。
平时用的最多的就是 json,还有一部分用的是xml,所以只总结了这两部分,别的格式用的非常少。
一、JSON
1.什么是JSON
概念:JavaScript Object Notation。 即‘JavaScript对象表示法’。简单来讲,JSON就是JavaScript的对象和数组的字符串表示法,它使用文本表示一个Js对象或数组的信息,因此,JSON的本质是字符串。
作用:JSON是一种轻量级的文本数据交换格式,在作用上类似于XML,专门用于存储和传输数据,但是JSON比XML更小、更快、更易解析。
2.java中常用的JSON转换的类库有 Jackson、Gson、FastJson。下面依次进行总结:
首先Jackson:
如果你的项目进行了前后端分离,那你一定使用过JSON进行数据交互,那在后端就一定会涉及到对Json数据的解析,虽然使用SpringMvc加上@requestBody都已经帮我们解析好并映射到bean里了,但是他底层也是通过这些JSON解析类库来完成的(SpringMVC默认使用的就是Jackson)。在我们后端直接调其他服务的接口时,很多也会返回JSON数据也需要我们自己使用这些类库来进行解析。
1)导入依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
2) ObjectMapper
ObjectMapper类(com.fasterxml.jackson.databind.ObjectMapper)是Jackson的主要类,它可以帮助我们快速的进行各个类型和Json类型的相互转换。也就是说我们使用Jackson类库进行JSON序列化时,主要其实就是通过ObjectMapper类进行的。
源码:com.fasterxml.jackson.databind.SerializationFeature
该类是一个枚举类型,只有一个boolean
类型的参数。即开启/禁用该设置。一般我们使用ObjectMapper objectMapper = new ObjectMapper();
创建出来的ObjectMapper
对象,实际上包含了SerializationFeature
类的默认配置。我们若想修改配置,可以使用下面的方法:
//SerializationFeature代表配置;state代表状态
public ObjectMapper configure(SerializationFeature f, boolean state)
//启用SerializationFeature配置
public ObjectMapper enable(SerializationFeature f)
//禁用配置
public ObjectMapper disable(SerializationFeature f)
3)ObjectMapper常用配置
按需配置,并不是所有都要配,配置太多也有可能会干扰我们正常的业务
/**
* 设置一些通用的属性
*/
static {
MAPPER = new ObjectMapper();
//序列化所有属性,对象中属性为null的时候,会打印该属性为null
MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//序列化时,对象中属性为null的时候,会忽略该属性
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//序列化时,若POJO对象的属性值为"",序列化时不进行显示
MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//序列化时,忽略值为默认值的属性
MAPPER.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
//美化输出,转换为格式化的json
// MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
//在遇到未知属性的时候不抛出异常。
MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//允许没有引号的字段名(非标准)出现。
MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
//允许单引号(非标准)
MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 允许整数以0开头
MAPPER.configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS.mappedFeature(), true);
// 允许出现特殊字符和转义符
//mapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);这个已经过时。
MAPPER.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
}
4)ObjectMapper特殊配置
关于Date类以及LocalDate配置:
首先关于Date日期类型的序列化,如果不进行配置,会默认序列化为时间戳的格式,这种一般不是我们需要的,所以需要配置一下我们需要的格式。
其次如果我们用的是LocalDateTime格式如果没有进行配置,会报错,所以也需要配置一下我们需要的格式:
//关于Date类型参数序列化配置
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// MAPPER.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
//关于localDateTime类型参数序列化配置,不配会报错
JavaTimeModule timeModule = new JavaTimeModule();
//反序列化
timeModule.addDeserializer(LocalDateTime.class, new
LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//系列化
timeModule.addSerializer(LocalDateTime.class, new
LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
MAPPER.registerModule(timeModule);
我们local的日期类型有三种分别是 LocalDateTime,LocalDate,LocalTime,一个一个配置比较麻烦,我们可以单独写一个配置类,查看JavaTimeModule 的源码可以发现他是继承了SimpleModule这个类,所以我们也可以继承这个类,实现日期的序列化配置,配置如下:
public class SatiJavaTimeModule extends SimpleModule {
public SatiJavaTimeModule() {
super(PackageVersion.VERSION);
// ======================= 时间序列化规则 ===============================
// yyyy-MM-dd HH:mm:ss
this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER));
// yyyy-MM-dd
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
// HH:mm:ss
this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
// Instant 类型序列化
this.addSerializer(Instant.class, InstantSerializer.INSTANCE);
// ======================= 时间反序列化规则 ==============================
// yyyy-MM-dd HH:mm:ss
this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER));
// yyyy-MM-dd
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
// HH:mm:ss
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
// Instant 反序列化
this.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
}
}
然后将这个配置类加入到ObjectMapper中即可:
//关于localDateTime类型参数序列化配置,不配会报错
// JavaTimeModule timeModule = new JavaTimeModule();
// //反序列化
// timeModule.addDeserializer(LocalDateTime.class, new
// LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// //系列化
// timeModule.addSerializer(LocalDateTime.class, new
// LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// MAPPER.registerModule(timeModule);
//上面的配置都不需要了,直接创建配置类即可
MAPPER.registerModule(new SatiJavaTimeModule());
关于SimpleModule,可以理解为他是一个自定义序列化器,可以序列化时自定义数据类型,我其实也比较陌生,然后搜索了一下,发现了一些常规用处:
1)自定义LocalDateTime类型参数序列化和反序列化,如上代码所示
2)除了自定义LocalDateTime之外还可以自定义 Long类型的参数转换为String类型,以防精度丢失,还有Integer和BigDecimal类型参数,实例如下:
SimpleModule simpleModule = new SimpleModule();
//将Long类型序列化为String类型
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
//将BigDecimal类型序列化为String类型
simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
MAPPER.registerModule(simpleModule);
以上是简单的扩展方法,如果想深入了解可自行搜索,如果有懂得老哥可以帮忙详细解释一下。
JsonSerializer<T>类介绍:
JsonSerializer<T>
是一个泛型类,用于自定义对象在序列化为JSON字符串时的行为。它允许您以自定义的方式控制对象序列化过程,包括属性命名、值格式、忽略字段等。不同的JSON库和框架提供了不同的实现方式。
简而言之,他也是可以在序列化时进行自定义的,简单用法,比如当BigDecimal类型的参数为0时,可以将其序列化为“0.00”,如下:
public class BigDecimalSerializer extends JsonSerializer<BigDecimal> {
@Override
public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers)
throws IOException{
if(value!=null) {
if(BigDecimal.ZERO.compareTo(value) == 0){
gen.writeString("0.00");
}else {
gen.writeString(value.toString());
}
}else {
gen.writeNull();
}
}
}
或者将Long类型序列化为String:
public class LongSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if(value!=null) {
jsonGenerator.writeString(value.toString());
}else {
jsonGenerator.writeNull();
}
}
}
继承JsonSerializer,然后重写serialize方法,将配置类放入
SimpleModule中即可:
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(BigDecimal.class,new BigDecimalSerializer());
simpleModule.addSerializer(Long.class, new LongSerializer());
MAPPER.registerModule(simpleModule);
,还可以指定某些类的自定义序列化,比如自定义person类:
public class PersonSerializer extends JsonSerializer<JJsonUtils.Person> {
@Override
public void serialize(JJsonUtils.Person person, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", person.getNameeee());
jsonGenerator.writeNumberField("age", person.getAgeeee());
jsonGenerator.writeEndObject();
}
}
将其放入ObjectMapper配置中,当序列化Person类时,就会按照这个配置生效。
5)ObjectMapper常用api
Student student = new Student("姓名","年龄");
///将对象转换为json字符串
String s = MAPPER.writeValueAsString(student);
System.out.println(s);
//将json字符串转换为对象
Student student1 = MAPPER.readValue("{\"name\":\"姓名\",\"age\":\"年龄\"}", Student.class);
System.out.println(student1);
System.out.println("---------------------------------------------------------");
//将对象转换为数组
byte[] bytes = MAPPER.writeValueAsBytes(student);
System.out.println(bytes);
//将字节数组转换为对象
Student student2 = MAPPER.readValue(bytes, Student.class);
System.out.println(student2);
System.out.println("---------------------------------------------------------");
//将列表转换成json字符串
List<Student> students = new ArrayList<>();
students.add(new Student("姓名","年龄"));
students.add(new Student("姓名","年龄"));
students.add(new Student("姓名","年龄"));
students.add(new Student("姓名","年龄"));
students.add(new Student("姓名","年龄"));
String s1 = MAPPER.writeValueAsString(students);
System.out.println(s1);
//将json字符串转成List列表
List list = MAPPER.readValue("[{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"},{" +
"\"name\":\"姓名\",\"age\":\"年龄\"}]", List.class);
System.out.println(list);
System.out.println("---------------------------------------------------------");
//将Map转成字符串
Map<Integer, Student> map = new HashMap<>();
map.put(1,new Student("姓名","年龄"));
map.put(2,new Student("姓名","年龄"));
map.put(3,new Student("姓名","年龄"));
map.put(4,new Student("姓名","年龄"));
map.put(5,new Student("姓名","年龄"));
String s2 = MAPPER.writeValueAsString(map);
System.out.println(s2);
//将字符串转成Map对象
Map map1 = MAPPER.readValue("{\"1\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"2\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"3\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"4\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"5\":{\"name\":\"姓名\",\"age\":\"年龄\"}}", Map.class);
System.out.println(map1);
在读取列表时,也就是说反序列化列表时,会遇到泛型擦除问题,所及建议使用官方推荐的TypeReference<T>来进行列表的读取,如下:
//将json字符串转成List列表
List<Student> list = MAPPER.readValue("[{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"{\"name\":\"姓名\",\"age\":\"年龄\"},{" +
"\"name\":\"姓名\",\"age\":\"年龄\"}]", new TypeReference<List<Student>>() {
});
System.out.println(list);
map也一样:
//将字符串转成Map对象
Map<Integer, Student> map1 = MAPPER.readValue("{\"1\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"2\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"3\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"4\":{\"name\":\"姓名\",\"age\":\"年龄\"}," +
"\"5\":{\"name\":\"姓名\",\"age\":\"年龄\"}}", new TypeReference<Map<Integer, Student>>() {
});
System.out.println(map1);
6)ObjectMapper其他api记录
convertValue
方法:
ObjectMapper 的 convertValue 方法在 Jackson 库中是用来进行对象转换的。这个方法可以将一种 Java 类型转换为另一种 Java 类型。例如,如果你有一个 Map 对象,你可能想要将它转换为一个更具体的对象。convertValue 方法可以帮助你完成这种转换。
CommonResp commonResp = MAPPER.readValue("{\"success\":true,\"errCode\":11,\"data\":{\"name\":\"姓名\",\"age\":\"年龄\"},\"dateTime\":\"2023-12-19 14:18:44\"}", CommonResp.class);
Student student = MAPPER.convertValue(commonResp.getData(), new TypeReference<Student>() {});
System.out.println(commonResp.getData());
System.out.println(student);
readTree()方法:
此方法更灵活,可以只将用户感兴趣的Json串信息值提取出来。主要利用ObjectMapper提供的readTree和Jackson提供的JsonNode类来实现.
也就是说,如果某个json串中含有多层嵌套的格式,但是我们只想提取某一个节点的数据,不想全部序列化成某个实体类。我们可以使用这个方法:
JsonNode jsonNode = MAPPER.readTree("{\"success\":true,\"errCode\":11,\"data\":{\"name\":\"姓名\",\"age\":\"年龄\"},\"dateTime\":\"2023-12-19 14:18:44\"}");
JsonNode data = jsonNode.get("data");
Student student = MAPPER.convertValue(data, Student.class);
System.out.println(data);
System.out.println(student);
将Json串以树状结构读入内存,再读出data节点下的信息,然后再转换为我们需要的Student类型对象。
jsonNode也可以使用下面的方法获取:
JsonNode jsonNode = MAPPER.readValue("{\"success\":true,\"errCode\":11,\"data\":{\"name\":\"姓名\",\"age\":\"年龄\"},\"dateTime\":\"2023-12-19 14:18:44\"}", JsonNode.class);
JsonNode提供了非常灵活和动态访问的方式, 可以像访问Java对象那样导航浏览JSON
如果把JSON解析为一个JsonNode实例(或一个JsonNode实例树), 就可以浏览JsonNode树模型, 如下例中用JsonNode访问JSON中的属性,数组,对象等等:
还有从别的地方读取json:
从JSON Reader对象读取Java对象
也可以通过Reader
实例从JSON中读取一个Java对象
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 4 }";
Reader reader = new StringReader(carJson);
Car car = objectMapper.readValue(reader, Car.class);
从JSON文件读取Java对象
也可以通过FileReader
实例从读取JSON(替换上例中的StringReader
即可), 当然, 也可以直接使用File
对象
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);
从URL获取JSON数据读取Java对象
可以通过URL(java.net.URL
)获取JSON数据后读取Java对象
ObjectMapper objectMapper = new ObjectMapper();
URL url = new URL("file:data/car.json");
Car car = objectMapper.readValue(url, Car.class);
这个例子使用了文件URL, 当然也可以使用HTTP URL
从Java InputStream获取JSON数据读取Java对象
ObjectMapper objectMapper = new ObjectMapper();
InputStream input = new FileInputStream("data/car.json");
Car car = objectMapper.readValue(input, Car.class);
ObjectMapper objectMapper = new ObjectMapper();
String carJson = "{ \"brand\" : \"Mercedes\", \"doors\" : 5 }";
byte[] bytes = carJson.getBytes("UTF-8");
Car car = objectMapper.readValue(bytes, Car.class);
从JSON数组字符串读取Java对象数组ObjectMapper
也可以从JSON数组字符串中读取一组Java对象
String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);
当然了, JSON源不仅是字符串, 也可以是文件, URL, InputStream, Reader等等
7)Jackson常用注解
序列化/反序列化都生效注解:
1)@JsonIgnore:一般会修饰Java类的属性上,无论序列化还是反序列化,Jackson都会忽略这个属性。
2)@JsonIgnoreProperties:作用和@JsonIgnore类似,但是@JsonIgnoreProperties修饰在Java类上,它可设置忽略多个属性,且可以设置ignoreUnknown = true,反序列化时,忽略在JSON中存在,但在Java类中不存在的字段,而不报异常。
3)@JsonIgnoreType:当其他类有该类作为属性时,该属性将被忽略。
4)@JsonProperty:如果JSON中字段名和Java类中的属性名不一致时,可以用@JsonProperty修饰在属性上。
5)@JsonAnyGetter和@JsonAnySetter:
@JsonAnyGetter
1.方法是非静态,没有参数的,方法名随意
2.方法返回值必须是Map类型
3.在一个实体类中仅仅用在一个方法上
4.序列化的时候json字段的key就是返回Map的key,value就是Map的value
@JsonAnySetter
1.用在非静态方法上,注解的方法必须有两个参数,第一个是json字段中的key,第二个是value,方法名随意
2.也可以用在Map对象属性上面,建议用在Map对象属性上面
3.反序列化的时候将对应不上的字段全部放到Map里面
仅序列化时生效注解:
1)@JsonFormat:在序列化日期/时间值时指定格式。
2)@JsonInclude:是非常重要的且常用的注解,它可以修饰在类名上或者属性上,但是一般为了更加细粒度的控制,都修饰在属性上。
3)@JsonPropertyOrder:在序列化的时候自定义属性输出顺序
4)@JsonView:是Jackson的一个很实用的注解,比如一个类的对象,要根据当前登录人的权限来分别序列化成他只能看到的字段。比如数据库里一条员工信息,老板和小组长的权限不同,应该看到的数据范围也不同。
5)@JsonRawValue:完全按照原样序列化属性的值
示例代码:
序列化/反序列化都生效注解示例:
@Data
//加上该注解,序列化反序列化时会忽略value中的字段,并且ignoreUnknown = true时反序列化时,忽略在JSON中存在,但在Java类中不存在的字段,而不报异常。
@JsonIgnoreProperties(value = {"age"}, ignoreUnknown = true)
static class AAA{
//加上该注解,无论序列化还是反序列化都会忽视该字段
@JsonIgnore
private String name;
private String age;
//序列化的字段名以该注解标注的值为准
@JsonProperty("time")
private LocalDateTime dateTime;
private BBB bbb;
private Map<String, Object> otherAttributes = new HashMap<>();
//反序列化的时候将对应不上的字段全部放到Map里面
@JsonAnyGetter
public Map<String, Object> getOtherAttributes() {
return this.otherAttributes;
}
//该注解可以将这个map中的数据,拼接到该类序列化后的属性当中,
//也就是说如果map有两个键值对,序列化之后会连同该类的字段一同展示出来
@JsonAnySetter
public void setOtherAttributes(String name, Object value) {
this.otherAttributes.put(name, value);
}
}
@Data
//被该注解注释的类,作为别的类的属性时,序列化反序列化都会被忽视
@JsonIgnoreType
static class BBB{
private String name;
private String age;
public LocalDateTime dateTime;
}
正常没有加任何注解时输出:
加上上述注解后输出:
仅序列化时生效注解:
@JsonFormat、@JsonPropertyOrder、@JsonRawValue示例:
@JsonFormat:指定时间格式,仅序列化时生效 LocalDateTime 跟 Date 都生效,当Mapper中已经配置了时间格式,并且也配置了该注解时,以该注解为准。
@JsonRawValue:如果不加该注解 json序列化的格式为: "json":"{\"attr\":false}" 加上之后序列化格式为: "json" : {"attr":false}
@Data
@NoArgsConstructor
@AllArgsConstructor
//指定序列化顺序
@JsonPropertyOrder(value = {"age", "name", "date","dateTime"})
static class CCC{
private String name;
private String age;
//指定时间格式,仅序列化时生效 LocalDateTime 跟 Date 都生效
//当Mapper中已经配置了时间格式,并且也配置了该注解时,以该注解为准
@JsonFormat(pattern = "yyyy年MM月dd日 HH时mm分ss秒", timezone = "GMT+8")
public LocalDateTime dateTime;
//指定时间格式,仅序列化时生效 LocalDateTime 跟 Date 都生效
//当Mapper中已经配置了时间格式,并且也配置了该注解时,以该注解为准
@JsonFormat(pattern = "yyyy年MM月dd日 HH时mm分ss秒", timezone = "GMT+8")
public Date date;
//完全按照原样序列化属性的值
@JsonRawValue
public String json;
}
@JsonInclude注解示例:
@Data
@NoArgsConstructor
@AllArgsConstructor
static class DDD{
//哪怕是null,也会参与序列化
@JsonInclude
private String name;
//非NULL
@JsonInclude(JsonInclude.Include.NON_NULL)
private String age;
//非NULL,非“”,
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String date;
//非NULL,非Optional.empty()
@JsonInclude(JsonInclude.Include.NON_ABSENT)
private Optional<String> sex;
//非NULL,集合isEmpty() = false
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<String> datetime;
//属性值为缺省值时,不序列化
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
private Integer job;
}
不加注解时序列化:
加上注解时序列化:
@JsonGetter
标记在get方法上,指定某个属性的 getter 方法:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
@JsonSerialize、@JsonDeserialize
指定某个属性使用的自定义序列化程序或反序列化程序。
例子:
- 让我们将使用 @JsonSerialize 通过 CustomDateSerializer 序列化 eventDate 属性:
public class EventWithSerializer {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
- 这是简单的自定义 Jackson 序列化程序:
public class CustomDateSerializer extends StdSerializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(
Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
@JsonCreator
可以使用 @JsonCreator 注解来调整反序列化中使用的构造函数/工厂。
使用场景:
当我们需要反序列化一些与我们需要获取的目标实体不完全匹配的JSON时,它非常有用
例子
- 反序列化的JSON字符串
{
"id":1,
"theName":"My bean"
}
- 但是,我们的目标实体中没有 theName 字段,只有一个 name 字段。现在我们不想更改实体本身,我们只需要通过使用 @JsonCreator, 构造函数并使用 @JsonProperty 注解来对解组过程进行更多控制:
public class BeanWithCreator {
public int id;
public String name;
@JsonCreator
public BeanWithCreator(
@JsonProperty("id") int id,
@JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}
@JsonValue
该注解作用于一个方法, 并且只用被注解的方法序列化整个实体对象
class ExtendableBean {
public String name;
public Map<String, String> properties;
@JsonValue
public Map<String, String> getProperties() {
return properties;
}
public ExtendableBean(String name) {
this.name = name;
properties = new HashMap<>();
}
public void add(String key, String value){
properties.put(key, value);
}
}
我们将 @JsonValue标注在getProperties方法上,序列化时,就会调用该方法,只序列化该方法中传递的属性。
private static void whenSerializingUsingJsonAnyGetter_thenCorrect(){
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = null;
try {
result = new ObjectMapper().writeValueAsString(bean);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(result);
}
输出结果是: {"attr2":"val2","attr1":"val1"},没有序列化name属性。
@JsonRootName
如果wrapping是使能(enabled), 那么该注解用来指定root wrapper的名称
wrapping(包装)的含义是如果序列化实体User的结果是:
{
"id": 1,
"name": "John"
}
那么wrapping后的效果如下:
{
"User": {
"id": 1,
"name": "John"
}
}
下面看一个例子, 我们用该注解指明包装实体(wrapper entity)的包装器名称:
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
包装器默认名称是实体类名, 这里就是UserWithRoot, 但是注解的value属性把包装器名称改为了user
序列化过程(和前面不同, 需要使能包装器)
private static void whenSerializingUsingJsonRootName_thenCorrect(){
UserWithRoot user = new UserWithRoot();
user.id = 1;
user.name = "jackma";
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = objectMapper.writeValueAsString(user);
System.out.println(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
序列化的结果:{"user":{"id":1,"name":"jackma"}}
从Jackson .2.4
版本开始, 新增了一个可选参数namespace
, 该属性对json没效果, 但是对xml起作用, 修改本例的实体例:
@JsonRootName(value = "user", namespace = "alibaba")
class UserWithRoot {
public int id;
public String name;
}
用XmlMapper序列化:
private static void whenSerializingUsingJsonRootName_thenCorrect(){
..............
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
..............
序列化结果:
<user xmlns="alibaba">
<id xmlns="">1</id>
<name xmlns="">jackma</name>
</user>
@JsonAlias
该注解在反序列化过程中为属性定义一个或多个别名
实体类:
public class AliasBean {
@JsonAlias({ "fName", "f_name" })
private String firstName;
private String lastName;
}
Json字符串中fName, f_name或firstName的值都可以被反序列到属性firstName
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
assertEquals("John", aliasBean.getFirstName());
}
@JsonAutoDetect
该注解可以覆盖属性是否可见的默认语义, 比如对于不可见的private序列化时变成可见的
实体类:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
private int id;
private String name;
}
序列化过程:
public void whenSerializingUsingJsonAutoDetect_thenCorrect() throws JsonProcessingException {
PrivateBean bean = new PrivateBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("My bean"));
}
@JsonUnwrapped
该注解指定值在序列化和反序列化时, 去除对应属性的外包装(根节点)
实体类:
public class UnwrappedUser {
public int id;
@JsonUnwrapped
public Name name;
public static class Name {
public String firstName;
public String lastName;
}
}
序列化过程:
public void whenSerializingUsingJsonUnwrapped_thenCorrect() throws JsonProcessingException, ParseException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, not(containsString("name")));
}
序列化结果:
{
"id":1,
"firstName":"John",
"lastName":"Doe"
}
@JsonView
该注解指明属性序列化和反序列时的视图级别(View)
视图类: 主要用于表明哪一级的实体类的属性会被序列化或反序列化
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
实体类:
class UserWithRoot {
@JsonView(Views.Public.class)
public int id;
@JsonView(Views.Public.class)
public String name;
@JsonView(Views.Internal.class)
public String school;
}
实例化过程:
public void whenSerializingUsingJsonView_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new UserWithRoot();
user.id = 1;
user.name = "bl";
user.school = "suide";
try {
System.out.println(new ObjectMapper().writerWithView(Views.Internal.class).writeValueAsString(user));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
本例中, school的视图级别是View.Internal类, 而序列化的映射器设定的视图显示级别是Views.Public类, 比school的类型高了一级, 所以序列化结果中没有school,
{"id":1,"name":"bl"}
而如果修改映射器的视图级别是Views.Internal类, 则序列化结果中包含school
{"id":1,"name":"bl","school":"suide"}
@JsonManagedReference, @JsonBackReference
这两个注解配合使用, 可以解决两个不同类的属性的父子关系(parent/child relationships)和循环引用(work around loops)
使用@JsonBackReference可以在序列化时阻断循环引用, 原理是忽略被注解的属性, 否则会导致异常
本例中, 我们用这组注解来序列化ItemWithRef实体类:
public class ItemWithRef {
public int id;
public String itemName;
@JsonManagedReference
public UserWithRef owner;
}
public class UserWithRef {
public int id;
public String name;
@JsonBackReference
public List<ItemWithRef> userItems;
}
序列化过程:
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect() throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
}
序列化结果:
{"id":2,"itemName":"book","owner":{"id":1,"name":"John"}}
如果把注解对调并序列化user结果是:
{"id":1,"name":"John","userItems":[{"id":2,"itemName":"book"}]}
@JsonIdentityInfo
该注解标明在序列化和反序列化一个值时, 该属性是否作为对象的唯一标识
该特性可以有效的解除循环引用, 和@JsonBackReference的区别是循环引用的对象的一个属性, 可以作为该对象的唯一标识被序列化, 而@JsonBackReference的循环引用对象不会二次序列化
两个实体类:
@JsonIdentityInfo( generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}
public class UserWithIdentity {
public int id;
public String name;
public List<ItemWithIdentity> userItems;
}
实例化过程:
public void whenSerializingUsingJsonIdentityInfo_thenCorrect() throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
}
序列化结果:{"id":2,"itemName":"book","owner":{"id":1,"name":"John","userItems":[2]}}
这里循环引用对象是ItemWithIdentity, 当它作为UserWithIdentity的属性时, 指定它的id属性为其唯一标识序列化到UserWithIdentity当中
@JsonFilter
该注解可以在序列化时指定一个过滤器
下面为一个实体类指定一个过滤器:
@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
定义过滤器并进行序列化
public void whenSerializingUsingJsonFilter_thenCorrect() throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper().writer(filters).writeValueAsString(bean);
}
序列化结果:{"name":"My bean"}
这里添加了一个SimpleBeanPropertyFilter.filterOutAllExcept过滤器, 该过滤器的含义是除name属性外, 其他属性都被过滤掉(不序列化)
5.1 @JsonAppend
该注解用来给一个被序列化的对象添加一个虚拟属性. 这个功能非常有用, 尤其是当我们想直接在Json字符串中添加额外的信息时, 不再需要修改类的定义. 举例来说, 它可以很方便的在Json文档中插入bean的版本信息, 而不需要bean提供对应的属性.
使用@JsonAppend注解的实体类:
@JsonAppend(attrs = {@JsonAppend.Attr(value = "version")})
public class BeanWithAppend {
private int id;
private String name;
// constructor, getters and setters
}
序列化过程:
BeanWithAppend bean = new BeanWithAppend(2, "Bean With Append Annotation");
ObjectWriter writer = mapper.writerFor(BeanWithAppend.class).withAttribute("version", "1.0");
String jsonString = writer.writeValueAsString(bean);
序列化结果: { "id": 2, "name": "Bean With Append Annotation", "version": "1.0" }
@JsonNaming
该注解用来在序列化时选择一个属性命名习惯来代替默认属性名. 注解参数value用来指定已有命名习惯, 或用户定义的命名习惯
除默认值(value=LOWER_CAMEL_CASE, 即驼峰命名法)外, Jackson库同时提供了4种内置的属性命名习惯:
- KEBAB_CASE: 属性名单词用短线分隔连接, 比如hello-world
- LOWER_CASE: 属性名用小写字母而且没有分隔符, 比如helloworld
- SNAKE_CASE: 属性名用小写字母而且用下划线做分隔符, 比如hello_world
- UPPER_CAMEL_CASE: 属性名所有单词用大写开头而且没有分隔符, 比如HelloWorld
下例中用SNAKE_CASE命名法, 将属性beanName名序列化为bean_name
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class NamingBean {
private int id;
private String beanName;
// constructor, getters and setters
}
序列化过程:
NamingBean bean = new NamingBean(3, "Naming Bean");
String jsonString = mapper.writeValueAsString(bean);
序列化结果: { "id": 3, "bean_name": "Naming Bean" }
@JsonPropertyDescription
Jackson的独立模块JSON Schema提供了创建Json信息表(Json schemas)来描述Java的类型信息. 信息表可用于输出我们期望的序列化Java对象, 或者在反序列化前验证Json文档(document)
注解@JsonPropertyDescription允许把人类可读的描述信息, 附加在要创建的Json信息表的description属性
实体类:
public class PropertyDescriptionBean {
private int id;
@JsonPropertyDescription("This is a description of the name property")
private String name;
// getters and setters
}
序列化过程: 这里生成Json信息表的同时为它附加了description属性:
SchemaFactoryWrapper wrapper = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(PropertyDescriptionBean.class, wrapper);
JsonSchema jsonSchema = wrapper.finalSchema();
String jsonString = mapper.writeValueAsString(jsonSchema);
序列化结果:
{
"type": "object",
"id": "urn:jsonschema:com:baeldung:jackson:annotation:extra:PropertyDescriptionBean",
"properties":
{
"name":
{
"type": "string",
"description": "This is a description of the name property"
},
"id":
{
"type": "integer"
}
}
}
@JsonPOJOBuilder
该注解用来配置一个builder类用于定制反序列化过程, 尤其是当Json文档中属性命名习惯和POJO类对象的属性不同
准备反序列化的Json字符串: { "id": 5, "name": "POJO Builder Bean"}
反序列化的目标类:
@JsonDeserialize(builder = BeanBuilder.class)
public class POJOBuilderBean {
private int identity;
private String beanName;
// constructor, getters and setters
}
注意:BeanBuilder是自定义bulider类, 参见下文.
可以看到, bean属性的名称和Json字符串中对应属性的名称不同. 这就是@JsonPOJOBuilder发挥作用的地方.
@JsonPOJOBuilder有两个参数:
- buildMethodName: 一个无参方法, 用来在绑定Json属性和bean属性后, 创建bean的实例
- withPrefix: 方法名前缀, 有该前缀的方法是用来匹配Json属性和bean的属性. 默认前缀是with
下面是BeanBuilder类定义:
@JsonPOJOBuilder(buildMethodName = "createBean", withPrefix = "construct")
public class BeanBuilder {
private int idValue;
private String nameValue;
public BeanBuilder constructId(int id) {
idValue = id;
return this;
}
public BeanBuilder constructName(String name) {
nameValue = name;
return this;
}
public POJOBuilderBean createBean() {
return new POJOBuilderBean(idValue, nameValue);
}
}
上面的代码中, 我们配置了注解@JsonPOJOBuilder的参数, 用createBean方法作为build方法, 用construct前缀来匹配属性名
反序列化过程:
String jsonString = "{\"id\":5,\"name\":\"POJO Builder Bean\"}";
POJOBuilderBean bean = mapper.readValue(jsonString, POJOBuilderBean.class);
@JsonTypeId
该注解作用于属性, 使得该属性不再是普通属性, 其值代表bean类的类型ID(`TypeId), 可以用它来描述多态时实体类对象的实际类型
实体类:
public class TypeIdBean {
private int id;
@JsonTypeId
private String name;
// constructor, getters and setters
}
序列化过程:
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
TypeIdBean bean = new TypeIdBean(6, "Type Id Bean");
String jsonString = mapper.writeValueAsString(bean);
序列化结果:["Type Id Bean",{"id":6}]
- mapper.enableDefaultTyping(DefaultTyping.NON_FINAL)的作用是在序列化结果中显示实体类类型属性
- 结果是一个Json对象, 其中"Type Id Bean"是实体类ID的描述, {"id":6}是类的属性值
禁用Jackson注解
通过设置MapperFeature.USE_ANNOTATIONS可以禁用实体类上的Jackson注解
实体类:
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
序列化过程:
public void whenDisablingAllAnnotations_thenAllDisabled() throws IOException {
MyBean bean = new MyBean(1, null);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
String result = mapper.writeValueAsString(bean);
}
- 序列化结果:{ "id":1, "name":null}
- 如果注释掉mapper.disable(MapperFeature.USE_ANNOTATIONS);, 则序列化结果是: {"id":1}
注解混合
多个实体类的注解可以混合在一起使用
下例中把类MyMixInForIgnoreType的注解@@JsonIgnoreType作用到了类Item的属性User:
public class Item {
public int id;
public String itemName;
public User owner;
}
@JsonIgnoreType
public class MyMixInForIgnoreType {}
序列化过程:
public void whenSerializingUsingMixInAnnotation_thenCorrect() throws JsonProcessingException {
Item item = new Item(1, "book", null);
String result = new ObjectMapper().writeValueAsString(item);
//结果: {"id":1,"itemName":"book","owner":null}
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);
result = mapper.writeValueAsString(item);
//结果: {"id":1,"itemName":"book"}
}
8)Spring Boot:自定义Jackson序列化、反序列化方式
spring有自己默认的jackson配置,所以我们跟前端联调时也需要修改一些序列化和、反序列化的配置。
1.注册一个新的SimpleModule类:
Spring Boot将自动注册任何类型为com.fasterxml.jackson.databind.Module的bean,会自动加载该bean中的配置:
@Configuration
@Slf4j
public class JacksonConfig {
public static LocalDateTimeSerializer LOCAL_DATETIME_SERIALIZER = new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER);
public static LocalDateTimeDeserializer LOCAL_DATETIME_DESERIALIZER = new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER);
public static LocalDateSerializer LOCAL_DATE_SERIALIZER = new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE);
public static LocalDateDeserializer LOCAL_DATE_DESERIALIZER = new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE);
public static LocalTimeSerializer LOCAL_TIME_SERIALIZER =new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME);
public static LocalTimeDeserializer LOCAL_TIME_DESERIALIZER = new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME);
@Bean
public Module javaTimeModule() {
SimpleModule module = new SimpleModule();
// ======================= 时间序列化规则 ===============================
// yyyy-MM-dd HH:mm:ss
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
// yyyy-MM-dd
module.addSerializer(LOCAL_DATE_SERIALIZER);
// HH:mm:ss
module.addSerializer(LOCAL_TIME_SERIALIZER);
// Instant 类型序列化
module.addSerializer(Instant.class, InstantSerializer.INSTANCE);
// ======================= 时间反序列化规则 ==============================
// yyyy-MM-dd HH:mm:ss
module.addDeserializer(LocalDateTime.class,LOCAL_DATETIME_DESERIALIZER);
// yyyy-MM-dd
module.addDeserializer(LocalDate.class,LOCAL_DATE_DESERIALIZER);
// HH:mm:ss
module.addDeserializer(LocalTime.class,LOCAL_TIME_DESERIALIZER);
// Instant 反序列化
module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
return module;
}
}
2.注册Jackson2ObjectMapperBuilderCustomizer
该功能接口的目的是允许我们创建配置Bean。 它们将应用于通过Jackson2ObjectMapperBuilder创建的默认ObjectMapper:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
.serializers(LOCAL_DATETIME_SERIALIZER)
.serializers(LOCAL_DATE_SERIALIZER)
.serializers(LOCAL_TIME_SERIALIZER)
.deserializers(LOCAL_DATETIME_DESERIALIZER)
.deserializers(LOCAL_DATE_DESERIALIZER)
.deserializers(LOCAL_TIME_DESERIALIZER);
}
3.重写ObjectMapper类型的bean
也就是说重新定义一个ObjectMapper bean并将其标记为@Primary,spring也会替换掉默认的ObjectMapper:
@Bean
@Primary
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule();
// ======================= 时间序列化规则 ===============================
// yyyy-MM-dd HH:mm:ss
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
// yyyy-MM-dd
module.addSerializer(LOCAL_DATE_SERIALIZER);
// HH:mm:ss
module.addSerializer(LOCAL_TIME_SERIALIZER);
// Instant 类型序列化
module.addSerializer(Instant.class, InstantSerializer.INSTANCE);
// ======================= 时间反序列化规则 ==============================
// yyyy-MM-dd HH:mm:ss
module.addDeserializer(LocalDateTime.class,LOCAL_DATETIME_DESERIALIZER);
// yyyy-MM-dd
module.addDeserializer(LocalDate.class,LOCAL_DATE_DESERIALIZER);
// HH:mm:ss
module.addDeserializer(LocalTime.class,LOCAL_TIME_DESERIALIZER);
// Instant 反序列化
module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
return new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.registerModule(module);
}
但是一般我们系统中使用同一个ObjectMapper,也就是我们自定义的ObjectMapper:
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper() {
return JsonUtils.getMapper();
}
4.Jackson2ObjectMapperBuilder
这种方法的优点是Jackson2ObjectMapperBuilder提供了一种简单直观的方法来构建ObjectMapper。底层也是注册、然后替换默认的。
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.serializers(LOCAL_DATETIME_SERIALIZER)
.serializers(LOCAL_DATE_SERIALIZER)
.serializers(LOCAL_TIME_SERIALIZER)
.deserializers(LOCAL_DATETIME_DESERIALIZER)
.deserializers(LOCAL_DATE_DESERIALIZER)
.deserializers(LOCAL_TIME_DESERIALIZER)
.serializationInclusion(JsonInclude.Include.NON_NULL);
}
9)相关代码:
@Slf4j
public class JsonUtils {
private static final ObjectMapper MAPPER;
/**
* 设置一些通用的属性
*/
static {
MAPPER = new ObjectMapper();
//序列化所有属性,对象中属性为null的时候,会打印该属性为null
MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//序列化时,对象中属性为null的时候,会忽略该属性
// MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//序列化时,若POJO对象的属性值为"",序列化时不进行显示
// MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//序列化时,忽略值为默认值的属性
// MAPPER.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
//美化输出,转换为格式化的json
MAPPER.enable(SerializationFeature.INDENT_OUTPUT);
//在遇到未知属性的时候不抛出异常。
MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//允许没有引号的字段名(非标准)出现。
MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
//允许单引号(非标准)
MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 允许整数以0开头
MAPPER.configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS.mappedFeature(), true);
// 允许出现特殊字符和转义符
//mapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);这个已经过时。
MAPPER.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
//关于Date类型参数序列化配置
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// MAPPER.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));
//关于localDateTime类型参数序列化配置,不配会报错
// JavaTimeModule timeModule = new JavaTimeModule();
// //反序列化
// timeModule.addDeserializer(LocalDateTime.class, new
// LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// //系列化
// timeModule.addSerializer(LocalDateTime.class, new
// LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// MAPPER.registerModule(timeModule);
//上面的配置都不需要了,直接创建配置类即可
MAPPER.registerModule(new SatiJavaTimeModule());
SimpleModule simpleModule = new SimpleModule();
//将Long类型序列化为String类型
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
//将BigDecimal类型序列化为String类型
simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
simpleModule.addSerializer(BigDecimal.class,new BigDecimalSerializer());
simpleModule.addSerializer(Long.class, new LongSerializer());
MAPPER.registerModule(simpleModule);
MAPPER.findAndRegisterModules();
}
public static ObjectMapper getMapper() {
return MAPPER;
}
public static String toJSONString(Object obj) {
return obj != null ? toJSONString(obj, () -> "", false) : "";
}
public static String toFormatJSONString(Object obj) {
return obj != null ? toJSONString(obj, () -> "", true) : "";
}
public static String toJSONString(Object obj, Supplier<String> defaultSupplier, boolean format) {
try {
if (obj == null) {
return defaultSupplier.get();
}
if (obj instanceof String) {
return obj.toString();
}
if (obj instanceof Number) {
return obj.toString();
}
if (format) {
return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
}
return MAPPER.writeValueAsString(obj);
} catch (Throwable e) {
log.error(String.format("toJSONString %s", obj != null ? obj.toString() : "null"), e);
}
return defaultSupplier.get();
}
public static <T> T toJavaObject(String value, Class<T> tClass) {
return StringUtils.isNotBlank(value) ? toJavaObject(value, tClass, () -> null) : null;
}
public static <T> T toJavaObject(Object obj, Class<T> tClass) {
return obj != null ? toJavaObject(toJSONString(obj), tClass, () -> null) : null;
}
public static <T> T toJavaObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) {
try {
if (StringUtils.isBlank(value)) {
return defaultSupplier.get();
}
return MAPPER.readValue(value, tClass);
} catch (Throwable e) {
log.error(String.format("toJavaObject exception: \n %s\n %s", value, tClass), e);
}
return defaultSupplier.get();
}
public static <T> List<T> toJavaObjectList(String value, Class<T> tClass) {
return StringUtils.isNotBlank(value) ? toJavaObjectList(value, tClass, () -> null) : null;
}
public static <T> List<T> toJavaObjectList(Object obj, Class<T> tClass) {
return obj != null ? toJavaObjectList(toJSONString(obj), tClass, () -> null) : null;
}
public static <T> List<T> toJavaObjectList(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier) {
try {
if (StringUtils.isBlank(value)) {
return defaultSupplier.get();
}
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, tClass);
return MAPPER.readValue(value, javaType);
} catch (Throwable e) {
log.error(String.format("toJavaObjectList exception \n%s\n%s", value, tClass), e);
}
return defaultSupplier.get();
}
// 简单地直接用json复制或者转换(Cloneable)
public static <T> T jsonCopy(Object obj, Class<T> tClass) {
return obj != null ? toJavaObject(toJSONString(obj), tClass) : null;
}
public static Map<String, Object> toMap(String value) {
return StringUtils.isNotBlank(value) ? toMap(value, () -> null) : null;
}
public static Map<String, Object> toMap(Object value) {
return value != null ? toMap(value, () -> null) : null;
}
public static Map<String, Object> toMap(Object value, Supplier<Map<String, Object>> defaultSupplier) {
if (value == null) {
return defaultSupplier.get();
}
try {
if (value instanceof Map) {
return (Map<String, Object>) value;
}
} catch (Exception e) {
log.info("fail to convert" + toJSONString(value), e);
}
return toMap(toJSONString(value), defaultSupplier);
}
public static Map<String, Object> toMap(String value, Supplier<Map<String, Object>> defaultSupplier) {
if (StringUtils.isBlank(value)) {
return defaultSupplier.get();
}
try {
return toJavaObject(value, LinkedHashMap.class);
} catch (Exception e) {
log.error(String.format("toMap exception\n%s", value), e);
}
return defaultSupplier.get();
}
public static List<Object> toList(String value) {
return StringUtils.isNotBlank(value) ? toList(value, () -> null) : null;
}
public static List<Object> toList(Object value) {
return value != null ? toList(value, () -> null) : null;
}
public static List<Object> toList(String value, Supplier<List<Object>> defaultSuppler) {
if (StringUtils.isBlank(value)) {
return defaultSuppler.get();
}
try {
return toJavaObject(value, List.class);
} catch (Exception e) {
log.error("toList exception\n" + value, e);
}
return defaultSuppler.get();
}
public static List<Object> toList(Object value, Supplier<List<Object>> defaultSuppler) {
if (value == null) {
return defaultSuppler.get();
}
if (value instanceof List) {
return (List<Object>) value;
}
return toList(toJSONString(value), defaultSuppler);
}
public static long getLong(Map<String, Object> map, String key) {
if (MapUtil.isEmpty(map)) {
return 0L;
}
String valueStr = String.valueOf(map.get(key));
if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
return 0L;
}
return Long.parseLong(valueStr);
}
public static int getInt(Map<String, Object> map, String key) {
if (MapUtil.isEmpty(map)) {
return 0;
}
String valueStr = String.valueOf(map.get(key));
if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
return 0;
}
return Integer.parseInt(valueStr);
}
}
@Configuration
public class JacksonMessageConverters {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper() {
return JsonUtils.getMapper();
}
}
@Configuration
@Slf4j
public class JacksonConfig {
public static LocalDateTimeSerializer LOCAL_DATETIME_SERIALIZER = new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER);
public static LocalDateTimeDeserializer LOCAL_DATETIME_DESERIALIZER = new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER);
public static LocalDateSerializer LOCAL_DATE_SERIALIZER = new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE);
public static LocalDateDeserializer LOCAL_DATE_DESERIALIZER = new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE);
public static LocalTimeSerializer LOCAL_TIME_SERIALIZER =new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME);
public static LocalTimeDeserializer LOCAL_TIME_DESERIALIZER = new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME);
@Bean
public Module javaTimeModule() {
SimpleModule module = new SimpleModule();
// ======================= 时间序列化规则 ===============================
// yyyy-MM-dd HH:mm:ss
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
// yyyy-MM-dd
module.addSerializer(LOCAL_DATE_SERIALIZER);
// HH:mm:ss
module.addSerializer(LOCAL_TIME_SERIALIZER);
// Instant 类型序列化
module.addSerializer(Instant.class, InstantSerializer.INSTANCE);
// ======================= 时间反序列化规则 ==============================
// yyyy-MM-dd HH:mm:ss
module.addDeserializer(LocalDateTime.class,LOCAL_DATETIME_DESERIALIZER);
// yyyy-MM-dd
module.addDeserializer(LocalDate.class,LOCAL_DATE_DESERIALIZER);
// HH:mm:ss
module.addDeserializer(LocalTime.class,LOCAL_TIME_DESERIALIZER);
// Instant 反序列化
module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
return module;
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
.serializers(LOCAL_DATETIME_SERIALIZER)
.serializers(LOCAL_DATE_SERIALIZER)
.serializers(LOCAL_TIME_SERIALIZER)
.deserializers(LOCAL_DATETIME_DESERIALIZER)
.deserializers(LOCAL_DATE_DESERIALIZER)
.deserializers(LOCAL_TIME_DESERIALIZER);
}
@Bean
@Primary
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule();
// ======================= 时间序列化规则 ===============================
// yyyy-MM-dd HH:mm:ss
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
// yyyy-MM-dd
module.addSerializer(LOCAL_DATE_SERIALIZER);
// HH:mm:ss
module.addSerializer(LOCAL_TIME_SERIALIZER);
// Instant 类型序列化
module.addSerializer(Instant.class, InstantSerializer.INSTANCE);
// ======================= 时间反序列化规则 ==============================
// yyyy-MM-dd HH:mm:ss
module.addDeserializer(LocalDateTime.class,LOCAL_DATETIME_DESERIALIZER);
// yyyy-MM-dd
module.addDeserializer(LocalDate.class,LOCAL_DATE_DESERIALIZER);
// HH:mm:ss
module.addDeserializer(LocalTime.class,LOCAL_TIME_DESERIALIZER);
// Instant 反序列化
module.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
return new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.registerModule(module);
}
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.serializers(LOCAL_DATETIME_SERIALIZER)
.serializers(LOCAL_DATE_SERIALIZER)
.serializers(LOCAL_TIME_SERIALIZER)
.deserializers(LOCAL_DATETIME_DESERIALIZER)
.deserializers(LOCAL_DATE_DESERIALIZER)
.deserializers(LOCAL_TIME_DESERIALIZER)
.serializationInclusion(JsonInclude.Include.NON_NULL);
}
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.serializers(LOCAL_DATETIME_SERIALIZER)
.serializers(LOCAL_DATE_SERIALIZER)
.serializers(LOCAL_TIME_SERIALIZER)
.deserializers(LOCAL_DATETIME_DESERIALIZER)
.deserializers(LOCAL_DATE_DESERIALIZER)
.deserializers(LOCAL_TIME_DESERIALIZER)
.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
}
SpringBoot教程(10) Jackson ObjectMapper使用和常用注解-CSDN博客
Jackson之注解大全_jackson 注解-CSDN博客
Jackson之注解大全_jackson 注解-CSDN博客
Spring Boot:自定义Jackson ObjectMapper_spring.jackson.default-property-inclusion-CSDN博客