Appearance
Jackson 2-使用注解进行json序列化与反序列化
在本篇文章中,我们主要介绍Jackson中提供的相关注解,以帮助我们更好地进行json序列化与反序列化。
参考文章链接:https://www.baeldung.com/jackson-annotations
0. 准备工作
首先准备一个商品类Goods:
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
private int id;
private String goodsCode;
private String goodsName;
private BigDecimal price;
private String spec;
private String unit;
private String remark;
}1. 序列化注解
注意,以下代码示例均
1.1 @JsonIgnore
@JsonIgnore标注在字段上,标识该字段不进行序列化。
java
// 在remark字段上标注 @JsonIgnore
@JsonIgnore
private String remark;java
@Test
void test01() throws JsonProcessingException {
Goods goods = new Goods(1, "A001", "矿泉水", new BigDecimal(2), "550ml", "瓶", "饮用天然矿泉水");
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(goods);
System.out.println(jsonString);
}结果json字符串中不包含remark字段:
txt
{"id":1,"goodsCode":"A001","goodsName":"矿泉水","price":2,"spec":"550ml","unit":"瓶"}1.2 @JsonIgnoreProperties
@JsonIgnoreProperties标识类中不进行序列化的字段。
java
@JsonIgnoreProperties(value = {"id","remark"}) // 表示id 和 remark 字段不进行序列化
public class Goods {
...
}测试代码复用,此处省略。测试结果如下:
txt
{"goodsCode":"A001","goodsName":"矿泉水","price":2,"spec":"550ml","unit":"瓶"}1.3 @JsonIncludeProperties
@JsonIncludeProperties标识类中需要进行序列化的字段,其余没包括的字段不进行序列化。
java
@JsonIncludeProperties(value = {"goodsCode", "goodsName"})
public class Goods {
...
}测试代码复用,此处省略。测试结果如下:
txt
{"goodsCode":"A001","goodsName":"矿泉水"}1.4 @JsonInclude
@JsonInclude可用于筛选出需要进行序列化的字段。
1.4.1 基础用法
例如,值null的字段不进行序列化。首先,将Goods的remark置为null:
java
Goods goods = new Goods(1, "A001", "矿泉水", new BigDecimal(2), "550ml", "瓶", null);然后在Goods类上进行标注:
java
@JsonInclude(value = JsonInclude.Include.NON_NULL)
public class Goods {
...
}结果如下:
txt
{"id":1,"goodsCode":"A001","goodsName":"矿泉水","price":2,"spec":"550ml","unit":"瓶"}1.4.2 自定义过滤器
@JsonInclude 注解中的value可设定值为JsonInclude.Include.CUSTOM,然后通过valueFilter自定义过滤器。自定义过滤器需实现 boolean equals(Object o)方法,当返回值为true时,表示该值不序列化,反之则进行序列化。例如:
java
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = GoodsFilter.class)
public class Goods {
...
}
class GoodsFilter {
public boolean equals(Object o) {
if(o instanceof BigDecimal){
BigDecimal bigDecimal = (BigDecimal) o;
return bigDecimal.compareTo(BigDecimal.ZERO) <= 0;
}
return false;
}
}上述自定义过滤器表示,如果价格小于等于零,则价格不序列化。
java
Goods goods = new Goods(1, "A001", "矿泉水", new BigDecimal(-2), "550ml", "瓶", null);txt
{"id":1,"goodsCode":"A001","goodsName":"矿泉水","spec":"550ml","unit":"瓶","remark":null}1.5 @JsonPropertyOrder
@JsonPropertyOrder可用于指定序列化后字段的顺序。
java
@JsonPropertyOrder({"goodsCode", "goodsName", "unit", "price", "spec", "remark"})
public class Goods {
...
}txt
{"goodsCode":"A001","goodsName":"矿泉水","unit":"瓶","price":2,"spec":"550ml","remark":"天然饮用矿泉水","id":1}未指定的字段顺序,将按照定义顺序。
我们也可以通过@JsonPropertyOrder(alphabetic = true)将字段顺序指定为字母表顺序。
1.6 @JsonSerialize
@JsonSerialize可用于指定自定义序列化器。
首先创建自己的序列化器:
java
class GoodsSerializer extends StdSerializer<Goods>{
public GoodsSerializer() {
super(Goods.class);
}
public GoodsSerializer(Class<Goods> t) {
super(t);
}
@Override
public void serialize(Goods value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("商品编码", value.getGoodsCode());
gen.writeStringField("商品名称", value.getGoodsName());
gen.writeStringField("价格", value.getPrice().toPlainString());
gen.writeStringField("规格", value.getSpec());
gen.writeStringField("单位", value.getUnit());
gen.writeStringField("备注", value.getRemark());
gen.writeEndObject();
}
}然后通过注解使用:
java
@JsonSerialize(using = GoodsSerializer.class)
public class Goods {
...
}结果如下:
txt
{"商品编码":"A001","商品名称":"矿泉水","价格":"2","规格":"550ml","单位":"瓶","备注":"天然饮用矿泉水"}2. 反序列化注解
2.1 @JsonAlias
@JsonAlias可用于在反序列化过程中,为字段提供别名。
java
@JsonAlias({"商品编码"})
private String goodsCode;
@JsonAlias({"商品名称"})
private String goodsName;java
@Test
void test03() throws JsonProcessingException {
String jsonString = "{\"商品编码\":\"A001\",\"商品名称\":\"矿泉水\",\"价格\":\"2\",\"规格\":\"550ml\",\"单位\":\"瓶\",\"备注\":\"天然饮用矿泉水\"}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Goods goods = objectMapper.readValue(jsonString, Goods.class);
System.out.println(goods);
}txt
Goods(id=0, goodsCode=A001, goodsName=矿泉水, price=null, spec=null, unit=null, remark=null)2.2@JsonDeserialize
@JsonDeserialize用于指定自定义反序列化器。
首先定义自己的反序列化器:
java
class GoodsDeserializer extends StdDeserializer<Goods> {
public GoodsDeserializer() {
super(Goods.class);
}
public GoodsDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Goods deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
TreeNode treeNode = p.readValueAsTree();
String goodsCode = ((TextNode)treeNode.get("商品编码")).asText();
String goodsName = ((TextNode)treeNode.get("商品名称")).asText();
String spec = ((TextNode)treeNode.get("规格")).asText();
String unit = ((TextNode)treeNode.get("单位")).asText();
String price = ((TextNode)treeNode.get("价格")).asText();
String remark = ((TextNode)treeNode.get("备注")).asText();
return new Goods(0, goodsCode, goodsName, new BigDecimal(price), spec, unit, remark);
}
}然后使用自定义的反序列化器:
java
@JsonDeserialize(using = GoodsDeserializer.class)
public class Goods {
...
}java
@Test
void test03() throws JsonProcessingException {
String jsonString = "{\"商品编码\":\"A001\",\"商品名称\":\"矿泉水\",\"价格\":\"2\",\"规格\":\"550ml\",\"单位\":\"瓶\",\"备注\":\"天然饮用矿泉水\"}";
ObjectMapper objectMapper = new ObjectMapper();
Goods goods = objectMapper.readValue(jsonString, Goods.class);
System.out.println(goods);
}3. 通用注解
3.1 @JsonProperty
@JsonProperty可用于修改json中的字段名称。
java
@JsonProperty("商品名称")
private String goodsName;txt
{"id":1,"goodsCode":"A001","price":2,"spec":"550ml","unit":"瓶","remark":"天然饮用矿泉水","商品名称":"矿泉水"}3.2 @JsonRootName
@JsonRootName用于在 JSON 序列化和反序列化过程中添加或移除根元素。简单来说,它会在生成的 JSON 字符串的最外层包裹一个以指定名称命名的对象。如果不指定名称,则默认名称是类名。
TIP
注意,如果要使该注解生效,则需要配置ObjectMapper对象。开启序列化配置SerializationFeature.WRAP_ROOT_VALUE和反序列化配置DeserializationFeature.UNWRAP_ROOT_VALUE。
java
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);java
@JsonRootName(value = "goods")
public class Goods {
...
}java
@Test
void test01() throws JsonProcessingException {
Goods goods = new Goods(1, "A001", "矿泉水", new BigDecimal(2), "550ml", "瓶", "天然饮用矿泉水");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
String jsonString = objectMapper.writeValueAsString(goods);
System.out.println(jsonString);
String anotherJsonString = "{\"goods\":{\"id\":1,\"goodsCode\":\"A001\",\"goodsName\":\"矿泉水\",\"price\":2,\"spec\":\"550ml\",\"unit\":\"瓶\",\"remark\":\"天然饮用矿泉水\"}}";
Goods goods1 = objectMapper.readValue(anotherJsonString, Goods.class);
System.out.println(goods1);
}3.3 @JsonFormat
@JsonFormat用于格式化日期时间类型,可用于Date类和Java 8 日期时间类型,例如LocalDateTime。
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Event {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
}java
@Test
void test04() throws JsonProcessingException {
Event event = new Event(new Date(), LocalDateTime.now());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
String jsonString = objectMapper.writeValueAsString(event);
System.out.println(jsonString);
}txt
{"date":"2024-12-15 09:12:07","localDateTime":"2024-12-15 17:12:07"}4. 多态注解
@JsonTypeInfo:类型信息注解,用于父类。属性如下:use:指定使用哪种方式包含类型信息。常用的值有:JsonTypeInfo.Id.CLASS:使用全限定类名;JsonTypeInfo.Id.NAME:使用逻辑名称,需配合@JsonSubTypes使用;
include:指定类型信息包含在哪里。常用的值有:JsonTypeInfo.As.PROPERTY:类型信息作为属性包含在对象中(常用)JsonTypeInfo.As.WRAPPER_OBJECT:类型信息作为键,对象作为值;
property:指定类型信息的名称,默认为全限定类名;
@JsonSubTypes:子类型注册,用于父类。属性是@JsonSubTypes.Type数组,@JsonSubTypes.Type的属性如下:value:子类型name:子类型的逻辑名称。
举例如下:
java
public interface Animal {
void makeSound();
}java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog implements Animal {
private String color;
@Override
public void makeSound() {
System.out.println("汪");
}
}java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cat implements Animal {
private String weight;
@Override
public void makeSound() {
System.out.println("喵");
}
}然后在父类中增加类型注解:
java
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public interface Animal {
void makeSound();
}测试代码如下:
java
@Test
void test01() throws JsonProcessingException {
Animal animal = new Dog("白");
ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(animal);
System.out.println(jsonString);
String anotherJsonString = "{\"type\":\"dog\",\"color\":\"黑\"}";
Animal animal1 = objectMapper.readValue(anotherJsonString, Animal.class);
System.out.println(animal1);
animal1.makeSound();
}结果如下:
txt
{"type":"dog","color":"白"}
Dog(color=黑)
汪