Jackson注解大全

2,256 阅读17分钟

转载自:Jackson注解大全

Jackson是当前用的比较广泛的,用来序列化和反序列化json的Java开源框架。Jackson社区相对比较活跃,更新速度也比较快, 从Github中的统计来看,Jackson是最流行的json解析器之一,Spring MVC、SprigBoot的默认json解析器都是Jackson。

Jackson优点很多:

  1. Jackson 所依赖的jar包较少,简单易用。
  2. 与其他 Java 的 json 的框架 Gson 等相比,Jackson 解析大的 json 文件速度比较快。
  3. Jackson 运行时占用内存比较低,性能比较好
  4. Jackson 有灵活的 API,可以很容易进行扩展和定制。

在本文中,我们将学习如何使用Jackson提供的现有注解 ,如何创建自定义注解 ,以及如何禁用Jackson注解 。

1、Jackson序列化注解

1.1、@JsonAnyGetter

@JsonAnyGetter注解 可以灵活地把类型为Map的属性作为标准属性序列化到JSON字符串中,有如下特点:

  • 方法为非静态方法,并且方法不带任何参数,方法名没有特殊约定(随意定义方法名称)
  • 方法返回值必须是Map类型
  • 在一个实体类中只能用在一个方法上
public class ExtendableUser {
    private String name;
    private Map<String, String> properties;

    public ExtendableUser() {
        properties = new HashMap<String, String>();
    }

    public ExtendableUser(String name) {
        this.name = name;
        properties = new HashMap<String, String>();
    }

    public void add( String key,  String value) {
        properties.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

编写测试用例:

@Test 
@DisplayName("测试@JsonAnyGetter")
void jsonAnyGetter() throws JsonProcessingException {
    ExtendableUser user = new ExtendableUser("Jack");
    user.add("province", "四川");
    user.add("city", "成都");
    String result = new ObjectMapper().writeValueAsString(user);
    System.out.println(result);
}

测试结果如下:

// 使用注解前
{
    "name":"Jack",
    "province":"四川",
    "city":"成都"
}
// 使用注解后
{
  "name": "Jack",
  "properties": {
    "province": "四川",
    "city": "成都"
  }
}

ExtendableUser 类的 map 中的属性提取出来当做了 bean 的标准属性序列化到JSON字符串中。在不改变Java类的前提下可以灵活地设置序列化后的JSON属性。

1.2、@JsonGetter

@JsonGetter注解 是**@JsonProperty**注解 的替代方法,用于将方法标记为getter方法。

在下面的例子中,我们指定getTheName()方法作为UserWithGetter实体的name属性的getter方法:

@NoArgsConstructor
@AllArgsConstructor
public class UserWithGetter {

    private String name;
    private int age;

    @JsonGetter("name")
    public String getTheName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

测试方法如下所示:

@Test 
@DisplayName("测试@JsonGetter")
void jsonGetter() throws JsonProcessingException {
    UserWithGetter bean = new UserWithGetter("Jack", 26);
    String result = new ObjectMapper().writeValueAsString(bean);
    System.out.println(result);
}

测试结果如下:

//不添加@JsonGetter("name")注解 
{"age":26,"theName":"Jack"}
//添加@JsonGetter("name")注解 
{"age":26,"name":"Jack"}

1.3、@JsonPropertyOrder

我们可以使用@JsonPropertyOrder注解 来指定序列化时各个属性的顺序。

定义User类,并自定义属性的顺序:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@JsonPropertyOrder({"name", "age", "id"})
public class User {

    private int id;
    private String name;
    private int age;
}

测试JSON输出:

    @Test 
    @DisplayName("测试@JsonPropertyOrder")
    void jsonPropertyOrder() throws JsonProcessingException {
        User user = new User(1, "Jack", 28);
        String result = new ObjectMapper().writeValueAsString(user);
        System.out.println(result);
    }

测试结果按照@JsonPropertyOrder指定的顺序输出:

{"name":"Jack","age":28,"id":1}

我们还可以使用@JsonPropertyOrder(alphabetic = true)按字母顺序对属性进行排序。在这种情况下,序列化输出结果是:

// 不带注解默认排序
{"id":1,"name":"Jack","age":28}
// 
{"age":28,"id":1,"name":"Jack"}

1.4、@JsonRawValue

如果对象中某个字段的值是 JSON,在使用Jackson序列化时,输出的JSON会有问题,这时我们可以使用注解 @JsonRawValue将JSON值原样输出。

定义测试实体类:

@Setter
@Getter
@AllArgsConstructor
public class RawUser {

    private String name;

    @JsonRawValue(value = false)
    private String address;

}

编写测试用例:

    @Test
    @DisplayName("测试@JsonRawValue")
    void jsonRawValue() throws JsonProcessingException {
        String jsonString = "{\"province\":\"广州\",\"city\":\"深圳\",\"area\":\"罗湖区\"}";
        RawUser rawUser = new RawUser("Tom", jsonString);
        String result = new ObjectMapper().writeValueAsString(rawUser);
        System.out.println(result);
    }
// 序列化输出结果为:
{
  "name": "Tom",
  "address": {
    "province": "广州",
    "city": "深圳",
    "area": "罗湖区"
  }
}
// 不添加@JsonRawValue注解时输出结果为
{
  "name": "Tom",
  "address": "{\"province\":\"广州\",\"city\":\"深圳\",\"area\":\"罗湖区\"}"
}

我们还可以使用可选的布尔参数来定义此注解 是否处于活动状态。

1.5、@JsonValue

@JsonValue可以用在字段或者字段对应的get方法上,当加上@JsonValue注解时,序列化返回的不再是JSON字符串,而是字段对应的值。一个类中只能有一个@JsonValue注解。

定义测试类UserJsonValue:

@AllArgsConstructor
@Getter
public class UserJsonValue {
    private String name;
    private int age;

    @JsonValue
    public String getName() {
        return name;
    }
}

编写测试用例测试:

    @Test
    @DisplayName("测试@JsonValue")
    void jsonValue() throws JsonProcessingException {
        UserJsonValue user = new UserJsonValue("John", 27);
        String result = new ObjectMapper().writeValueAsString(user);
        System.out.println(result);
    }

输出结果:

// 使用注解
"John"
// 不使用注解
{"name":"John","age":27}

1.6、@JsonRootName

正常情况下,我们定义一个User实体,并序列生成JSON字符串,输出结果如下:

{
    "id":1,
    "name":"John"
}

但是,在某些情况下我们需要将JSON包装成如下形式:

{
    "user":{
        "id":1,
        "name":"John"
    }
}

针对以上问题,Jackson提供了很好的解决方案,即使用@JsonRootName注解。使用@JsonRootName注解指定要包装的root名称,并在ObjectMapper实例中启用包装。

定义包装实体:

@AllArgsConstructor
@Getter
@JsonRootName(value = "user")
public class UserWithRoot {
    private int id;
    private String name;
}

编写测试用例进行测试:

    @Test
    @DisplayName("测试@JsonRootName")
    void jsonRootName() throws JsonProcessingException {
        UserWithRoot user = new UserWithRoot(1, "John");

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
        String result = mapper.writeValueAsString(user);
        System.out.println(result);
    }

测试结果如下:

{
  "user": {
    "id": 1,
    "name": "John"
  }
}

可选参数namespace,可用于XML等数据格式。如果添加它,就为xml前缀赋予了一个与某个命名空间相关联的限定名称:

@AllArgsConstructor
@Getter
@JsonRootName(value = "user", namespace = "users")
public class UserWithRootNamespace {
    private int id;
    private String name;
}

序列化测试用例:

    @Test
    @DisplayName("测试@JsonRootNameWithNameSpace")
    void xmlRootNameWithNameSpace() throws JsonProcessingException {
        UserWithRootNamespace author = new UserWithRootNamespace(1, "John");
        ObjectMapper mapper = new XmlMapper();
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE)
                .enable(SerializationFeature.INDENT_OUTPUT);
        String result = mapper.writeValueAsString(author);

        System.out.println(result);
    }

使用XmlMapper对其进行序列化,输出结果如下:

<user xmlns="users">
  <id xmlns="">1</id>
  <name xmlns="">John</name>
</user>

1.7、@JsonSerialize

@JsonSerialize表示在序列化时要使用自定义序列化器。

我们定义一个需要序列化的实体Person,使用自定义序列化器:

public class Person {
    private String name;
    @JsonSerialize(using = CustomDateSerializer.class)
    private Date birthday;
   //省略 constructor  getters setters
}

自定义Jackson序列化器:

public class CustomDateSerializer extends JsonSerializer<LocalDate> {

    private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        gen.writeString(value.format(dateTimeFormatter));
    }
}

编写测试用例:

    @Test
    @DisplayName("测试@JsonSerialize")
    void jsonSerialize() throws JsonProcessingException {
        PersonWithSerializer person = new PersonWithSerializer("Tom", LocalDate.now());
        String result = mapper.writeValueAsString(person);
        System.out.println(result);
    }

输出结果如下:

{
  "name" : "Tom",
  "birthday" : "2022年11月10日"
}

2、Jackson反序列化注解

2.1、@JsonCreator

JSON反序列化为java对象时,@JsonCreator 注解用于定义构造函数。反序列化时,默认选择类的无参构造函数创建对象,当没有无参构造函数时会报错,@JsonCreator作用就是指定反序列化时用的无参构造函数。构造方法的参数前面需要加上@JsonProperty,否则会报错。

当我们需要反序列化某些与我们需要获取的目标实体不完全匹配的JSON时,此注解非常有用。

我们需要反序列化以下JSON:

{
    "id":1,
    "theName":"Jack"
}

但是,目标实体中没有theName字段,只有一个name字段。而我们不想更改实体本身,Jackson针对此问题提供了很好的解决方案。我们只要使用*@JsonCreator注解 构造函数并同时使用@JsonProperty*注解 构造函数参数:

@Getter
@ToString
public class UserWithCreator {
    private int id;
    private String name;

    @JsonCreator
    public UserWithCreator(@JsonProperty("id") int id, @JsonProperty("theName")String name) {
        this.id = id;
        this.name = name;
    }
}

编写测试用例,验证结果:

    @Test
    @DisplayName("测试@JsonCreator")
    void jsonCreator() throws IOException {
        String json = "{\"id\":1,\"theName\":\"Jack\"}";
        UserWithCreator user = mapper.readerFor(UserWithCreator.class).readValue(json);
        System.out.println(user);
    }

输出结果如下:

UserWithCreator(id=1, name=Jack)

2.2、@JacksonInject

在反序列化时,如果JSON中的字段少于目标java对象中的字段,被序列化后的对象将缺少字段设置为字段的默认值,而在某些需求中,我们希望自定义默认值,这时候我们就可以用到这个注解了,它的功能就是在反序列化的时候将没有的字段设置为我们期望的默认值。

定义反序列化实体类:

@ToString
@Getter
public class UserWithInject {
    @JacksonInject
    private int id;
    private String name;
    @JacksonInject
    private String address;
}

定义测试用例:

    @Test
    @DisplayName("测试@JacksonInject")
    void jsonInject() throws IOException {
        String json = "{\"name\":\"Jack\"}";
        InjectableValues.Std injectableValues = new InjectableValues.Std();
        injectableValues.addValue(String.class,"北京");
        injectableValues.addValue(int.class, 1);
        UserWithInject user = mapper.reader(injectableValues).forType(UserWithInject.class).readValue(json);
        System.out.println(user);
    }

输出结果如下:

ExtendableUser(name=Jack, properties={province=四川, city=成都})

2.3、@JsonAnySetter

一般情况下对象属性名都是确定的, 但有时候我们还需要给对象设定一些扩展属性, 这些扩展属性名称暂时不好确定, 通常使用 Map来存放这些扩展属性,针对此问题Jackson也提供了很好的解决方案。

要把JSON数据中的扩展属性反序列化到类的Map中, 需要在类中增加一个K/V的setter方法, 而且这个setter方法要加上@JsonAnySetter注解。

@JsonAnySette注意事项:

  1. 此注解用在非静态方法上,注解的方法必须有两个参数,第一个是json字段中的key,第二个是value,方法名随意。
  2. 此注解也可以用在Map对象属性上面,建议用在Map对象属性上面。
  3. 反序列化的时候将对应不上的字段全部放到Map里面。

使用*@JsonAnySetter反序列化实体ExtendableUser*:

@ToString
@Getter
public class ExtendableUser {
    private String name;
    @JsonAnySetter
    private Map<String, String> properties;

    public ExtendableUser() {
        properties = new HashMap<>();
    }

    public ExtendableUser(String name) {
        this.name = name;
        properties = new HashMap<>();
    }
}

如下是需要反序列化的JSON:

{
    "name":"Jack",
    "province":"四川",
    "city":"成都"
}

编写测试用例测试:

    @Test
    @DisplayName("测试@JsonAnySetter")
    void jsonAnySetter() throws IOException {
        String json = "{\"name\":\"Jack\",\"province\":\"四川\",\"city\":\"成都\"}";
        ExtendableUser user = mapper.readerFor(ExtendableUser.class).readValue(json);
        System.out.println(user);
    }

测试结果如下:

ExtendableUser{name='Jack', properties={province=四川, city=成都}}

2.4、@JsonSetter

@JsonSetter 标注于 setter 方法上,类似 @JsonProperty ,也可以解决 json 键名称和 java 对象 字段名称不匹配的问题。

我们将在UserWithGetter实体中将方法s etTheName()指定为name属性的setter方法:

@ToString
public class UserWithSetter {

    private String name;
    private int age;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

编写测试用例测试:

    @Test
    @DisplayName("测试@JsonSetter")
    void jsonSetter() throws IOException {
        String json = "{\"name\":\"Jack\",\"age\":28}";
        UserWithSetter user = mapper.readerFor(UserWithSetter.class).readValue(json);
        System.out.println(user);
    }

测试结果如下:

ExtendableUser{name='Jack', properties={province=四川, city=成都}}

2.5、@JsonDeserialize

@JsonDeserialize注解指定使用自定义反序列化器。

定义反序列化类Person:

@Getter
@ToString
public class PersonWithDeserializer {
    private String name;
    @JsonDeserialize(using = CustomDateDeserializer.class)
    private LocalDate birthday;
}

自定义反序列化器:

public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {

    private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

    @Override
    public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return LocalDate.parse(p.getText(), dateTimeFormatter);
    }
}

测试代码如下:

    @Test
    @DisplayName("测试@JsonDeserialize")
    void jsonDeserialize() throws IOException {
        String json = "{\"name\":\"Jack\",\"birthday\":\"2021年05月20日\"}";
        PersonWithDeserializer person = mapper.readerFor(PersonWithDeserializer.class).readValue(json);
        System.out.println(person);
    }

输出结果如下:

PersonWithDeserializer(name=Jack, birthday=2021-05-20)

2.6、@JsonAlias

@JsonAlias注解定义在反序列化过程中给属性设置一个或者多个别名

定义AliasBean类来演示:

@Getter
@ToString
public class AliasUser {

    @JsonAlias({ "fName", "f_name" })
    private String firstName;

    private String lastName;
}

我们给AliasUser类的字段firstName,定义了两个别名fName和f_name。在反序列化的过程中,如果JSON字段中存在fName或者f_name,都会将值映射到AliasUser类的firstName字段中。

测试用例代码如下:

    @Test
    @DisplayName("测试@JsonAlias")
    void jsonAlias() throws IOException {
        String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
        AliasUser aliasUser = mapper.readerFor(AliasUser.class).readValue(json);
        System.out.println(aliasUser);
    }

输出结果如下:

PersonWithDeserializer(name=Jack, birthday=2021-05-20)

3、Jackson属性注解

3.1、@JsonIgnoreProperties

@JsonIgnoreProperties是一个类级别的注解 ,作用是将java对象序列化为JSON时,忽略的一个或多个属性。

以下示例,序列化时忽略UserWithIgnore类的属性ID

@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
@JsonIgnoreProperties({"id"})
public class UserWithIgnore {
    private Long id;
    private String name;
    private Integer age;
}

编写测试代码:

    @Test
    @DisplayName("测试@JsonIgnoreProperties")
    void jsonIgnoreProperties() throws JsonProcessingException {
        UserWithIgnore bean = new UserWithIgnore(1L, "Jack",17);
        String result = mapper.writeValueAsString(bean);
        System.out.println(result);
    }

输出结果如下:

{
  "name" : "Jack",
  "age" : 17
}

3.2、@JsonIgnore

@JsonIgnore是一个字段级别的注解 ,作用是在java对象序列化为JSON时,忽略此字段。

使用@JsonIgnore来忽略序列化的属性id:

@AllArgsConstructor
@NoArgsConstructor
@ToString
@Getter
public class UserWithIgnore {
    @JsonIgnore
    private Long id;
    private String name;
    private Integer age;
}

测试代码如下:

    @Test
    @DisplayName("测试@JsonIgnore")
    void jsonIgnore() throws JsonProcessingException {
        UserWithIgnore bean = new UserWithIgnore(1L, "Jack",17);
        String result = mapper.writeValueAsString(bean);
        System.out.println(result);
    }

输出结果如下:

{
  "name" : "Jack",
  "age" : 17
}

3.3、@JsonIgnoreType

@JsonIgnoreType注解 要忽略注解 类型的所有属性。

@NoArgsConstructor
@AllArgsConstructor
public class UserWithIgnoreType {
    public int id;

    public Name name;

    @NoArgsConstructor
    @AllArgsConstructor
    @JsonIgnoreType
    public static class Name {
        public String firstName;
        public String lastName;
    }
}

测试代码如下:

    @Test
    @DisplayName("测试@JsonIgnoreType")
    void jsonIgnoreType() throws JsonProcessingException {
        UserWithIgnoreType.Name name = new UserWithIgnoreType.Name("zhang", "san");
        UserWithIgnoreType user = new UserWithIgnoreType(1, name);
        String result = mapper.writeValueAsString(user);
        System.out.println(result);
    }

输出结果如下:

{"id":1}

3.4、@JsonInclude

我们可以使用@JsonInclude排除具有empty/null/默认值的属性。

如下示例演示序列化时排除null值:

@JsonInclude(Include.NON_NULL)
public class IncludeUser {
    public int id;
    public String name;
    public int age;
    public String address;
 //省略 constructor  getters setters
}

测试:

@Test
public void jsonInclude() throws JsonProcessingException {
    IncludeUser user = new IncludeUser();
    user.setId(1);
    String result = new ObjectMapper().writeValueAsString(user);
    System.out.println(result);
}

输出结果如下:

// 加了注解
{
  "id" : 1,
  "age" : 0
}
// 不加注解
{
  "id" : 0,
  "name" : null,
  "age" : 0,
  "address" : null
}

3.5、@JsonAutoDetect

我们定义的类中的所有属性由private修饰,不提供对应的get,set方法,如果按照默认的属性发现规则我们将无法序列化和反序列化类的字段(如果没有get,set方法,只有被public修饰的属性才会被发现)。这时,我们可以通过修改@JsonAutoDetect的fieldVisibility来调整自动发现级别,为了使字段被自动发现,我们需要将级别调整为ANY。

来看一个简单的示例:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
@AllArgsConstructor
@NoArgsConstructor
public class PrivateUser {
    private int id;
    private String name;
}

测试代码如下:

    @Test
    @DisplayName("测试@JsonAutoDetect")
    void jsonAutoDetect() throws JsonProcessingException {
        PrivateUser bean = new PrivateUser(1, "Jack");
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        String result = mapper.writeValueAsString(bean);
        System.out.println(result);
    }

输出结果:

{
  "id" : 1,
  "name" : "Jack"
}

4、Jackson常用注解

4.1、@JsonProperty

@JsonProperty 注解用于属性上,作用是把该属性的名称序列化为另外一个名称,例如将theName属性序列化为name,使用@JsonProperty("name")注解 。

@JsonProperty不仅仅是在序列化的时候有用,反序列化的时候也有用,比如有些接口返回的是json字符串,命名又不是标准的驼峰形式,在映射成对象的时候,在类的属性上加上@JsonProperty注解,里面写上返回的json串对应的名字即可。

定义实体类:

@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserWithGetter {

    @JsonProperty("theName")
    private String name;

    private int age;

}

测试并验证结果:

    @Test
    @DisplayName("测试@JsonProperty")
    public void JsonProperty() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        UserWithGetter bean = new UserWithGetter("Jack", 26);
        String result = mapper.writeValueAsString(bean);
        System.out.println(result);

        String json = "{\"theName\":\"Jack\",\"age\":26}";
        UserWithGetter bean1 = mapper.readerFor(UserWithGetter.class).readValue(json);
        System.out.println(bean1.toString());
    }

测试结果:

{"age":26,"theName":"Jack"}
UserWithGetter(name=Jack, age=26)

4.2、@JsonFormat

@JsonFormat注解指定序列化日期和时间的格式

使用*@JsonFormat来控制属性birthday*的格式:

@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class PersonWithFormat {
    private String name;

    @JsonFormat(pattern = "yyyy年MM月dd日 HH时mm分ss秒", timezone = "GMT+8")
    private LocalDateTime birthday;

}

测试:

    @Test
    @DisplayName("测试@JsonFormat")
    void jsonFormat() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 序列化设置时间格式
        // 全局配置
        // javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
        // // 反序列化设置时间格式
        // javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));
        mapper.registerModule(javaTimeModule);

        DateTimeFormatter df = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss", Locale.CHINA);

        String toParse = "25-10-2016 12:00:59";
        LocalDateTime dateTime = LocalDateTime.parse(toParse, df);

        PersonWithFormat person = new PersonWithFormat("Jack", dateTime);

        System.out.println(person);
        String result = mapper.writeValueAsString(person);
        System.out.println(result);

        String json = "{\"name\":\"Jack\",\"birthday\":\"2016年10月25日 12时00分59秒\"}";
        PersonWithFormat bean1 = mapper.readerFor(PersonWithFormat.class).readValue(json);
        System.out.println(bean1);
    }

输出结果:

PersonWithFormat(name=Jack, birthday=2016-10-25T12:00:59)
{"name":"Jack","birthday":"2016年10月25日 12时00分59秒"}
PersonWithFormat(name=Jack, birthday=2016-10-25T12:00:59)

4.3、@JsonUnwrapped

@JsonUnwrapped定义了在序列化/反序列化时,将嵌套的对象属性,序列化到JSON的同一级。

定义UnwrappedUser实体:

@AllArgsConstructor
public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    @AllArgsConstructor
    @NoArgsConstructor
    public static class Name {
        public String firstName;
        public String lastName;
    }
}

序列化过程:

    @Test
    @DisplayName("测试@JsonUnwrapped")
    void jsonUnwrapped() throws JsonProcessingException {
        UnwrappedUser.Name name = new UnwrappedUser.Name("li", "bai");
        UnwrappedUser user = new UnwrappedUser(1, name);

        String result = new ObjectMapper().writeValueAsString(user);
        System.out.println(result);
    }

观察输出结果:嵌套类的字段与其他字段一起展开:

{
  "id": 1,
  "firstName": "li",
  "lastName": "bai"
}

4.4、@JsonView

@JsonView注解作用是将java对象序列化为JSON时,过滤敏感字段或者不需要字段,使用步骤如下:

定义序列化实体UserInfo:

@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {

    public interface WithoutPasswordView {
    }

    public interface WithPasswordView extends WithoutPasswordView {
    }

    @JsonView(WithoutPasswordView.class)
    private String username;
    @JsonView(WithPasswordView.class)
    private String password;
    @JsonView(WithoutPasswordView.class)
    private Integer age;

}

最后,测试:

@Test
@DisplayName("测试@JsonView")
void jsonView() throws JsonProcessingException {
    UserInfo item = new UserInfo("Tom", "123456", 27);

    String result = new ObjectMapper().writerWithView(UserInfo.WithoutPasswordView.class).writeValueAsString(item);
    System.out.println(result);

    result = new ObjectMapper().writerWithView(UserInfo.WithPasswordView.class).writeValueAsString(item);
    System.out.println(result);
}

输出结果:

{"username":"Tom","age":27}
{"username":"Tom","password":"123456","age":27}

4.5、@JsonManagedReference,@JsonBackReference

这两个注解通常配对使用, 可以解决两个不同类的属性的父子关系和循环引用。

在序列化的时候,@JsonManagedReference 注解的属性则会被序列化,@JsonBackReference 的注解将会被忽略。

@JsonManagedReference 注解,通常说明这个字段是一个双向引用的字段,这个字段在这个双向引用中的角色为 “父”,与这个字段对应的引用需要注解为 @JsonBackReference

@JsonBackReference 注解,通常说明这个字段是关联的一个双向引用字段,这个字段在这个双向引用的角色是 “孩子”。这个字段的值只能是对象(Bean),不能是 集合(Collection),图(Map),数组(Array)和枚举类型(enumeration)。

定义ItemWithRef实体

@NoArgsConstructor
@AllArgsConstructor
@Getter
@ToString
public class ItemWithRef {
    public int id;
    public String itemName;

    @JsonManagedReference
    public UserWithRef owner;
}

UserWithRef实体

@NoArgsConstructor
@Getter
@ToString
public class UserWithRef {
    private int id;
    private String name;

    @JsonBackReference
    private List<ItemWithRef> userItemList;

    public UserWithRef(int id, String name) {
        this.id = id;
        this.name = name;
        userItemList = new ArrayList<>();
    }

    public void addItem(ItemWithRef item){
        userItemList.add(item);
    }
}

测试:

    @Test
    @DisplayName("测试jacksonReferenceAnnotation")
    void jacksonReferenceAnnotation() throws JsonProcessingException {
        UserWithRef user = new UserWithRef(1, "John");
        ItemWithRef item = new ItemWithRef(2, "book", user);
        user.addItem(item);
        String result = mapper.writeValueAsString(item);
        System.out.println(result);
        System.out.println(mapper.writeValueAsString(user));
    }

输出结果:

{"id":2,"itemName":"book","owner":{"id":1,"name":"John"}}

4.6、@JsonIdentityInfo

@JsonIdentityInfo注解,作用于类或属性上,被用来在序列化/反序列化时为该对象或字段添加一个对象识别码,通常是用来解决循环嵌套的问题。定义两个实体,ItemWithIdentity实体

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ItemWithIdentity {
    public int id;
    public String itemName;
    public UserWithIdentity owner;
}

UserWithIdentity实体:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class UserWithIdentity {
    public int id;
    public String name;
    public List<ItemWithIdentity> userItems;
}

测试:

@Test
public void  jsonIdentityInfo() throws JsonProcessingException {
    UserWithIdentity user = new UserWithIdentity(1, "John");
    ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);
    System.out.println(result);
}

输出结果:

{
  "id" : 2,
  "itemName" : "book",
  "owner" : {
    "id" : 1,
    "name" : "John"
  }
}
{
  "id" : 1,
  "name" : "John"
}

4.7、@JsonFilter

@JsonFilter注解的作用是在序列化的过程中过滤掉指定的属性,类似于@JsonIgnore注解。但是当有一堆字段需要配置,而且整个项目都需要统一处理的时候,@JsonFilter比较适合。

定义实体,然后指向过滤器:

@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@JsonFilter("myFilter")
public class UserWithFilter {
    private Long id;
    private String name;
    private Integer age;
}

测试代码:

    @Test
    @DisplayName("测试@JsonFilter")
    void jsonFilter() throws JsonProcessingException {
        UserWithFilter bean = new UserWithFilter(1000L, "Jack",19);

        //此过滤器的含义是除name属性外, 其他属性都被过滤掉(不序列化)
        FilterProvider filters = new SimpleFilterProvider()
                .addFilter("myFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name"));

        String result = mapper.writer(filters).writeValueAsString(bean);
        System.out.println(result);
    }

输出结果:

{
  "name" : "Jack"
}

5、自定义Jackson注解

使用@JacksonAnnotationsInside创建自定义的Jackson注解 。

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "age","id" })
public @interface CustomAnnotation {
}

定义实体并使用新注解 :

@CustomAnnotation
public class UserCustomAnnotation {
    private int id;
    private String name;
    private int age;
 //省略 constructor  getters setters
}

测试自定义注解 :

@Test //测试自定义注解 
public void customAnnotation() throws JsonProcessingException {
    UserCustomAnnotation bean = new UserCustomAnnotation(1, "Jack", 28);
    String result = new ObjectMapper().writeValueAsString(bean);
    System.out.println(result);
}

序列化输出:

//添加注解 前
{"id":null,"name":"Jack","age":28}
//添加注解 后
{"age":28,"name":"Jack"}

6、Jackson MixIn注解

Jackson mixins 是一种在类中添加 Jackson 注解而不修改实际类的机制。 它是为我们无法修改类的情况而创建的,例如在使用第三方类时。

我们可以使用任何Jackson注释,但不直接将它们添加到类中。我们在mixin类中使用它们,而mixin类既可以是抽象类,也可以是接口。它们既可以用于Jackson序列化也可以用于反序列化,并且必须将它们添加到ObjectMapper配置中。

当我们需要使用无法由 Jackson 序列化或反序列化的第三方类时,因为它们不遵循 Jackson 约定。 由于我们无法修改这些类,我们必须使用 mixins 来添加 Jackson 序列化所需的所有必要部分。

假设我们要序列化这个第三方类:

我们不能用 Jackson 序列化这个类,因为属性是私有的并且没有 getter 和 setter。 因此,Jackson 不会识别任何属性并会抛出异常:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.grabanotherbyte.jackson.Person and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
@AllArgsConstructor
@ToString
public class Person {
    private final String firstName;
    private final String lastName;
}

现在让我们创建一个 mixin 来解决这个问题! 在我们的 mixin 中,我们将添加我们想要序列化的属性:

public abstract class PersonMixIn {
    @JsonProperty
    private String firstName;
    @JsonProperty private String lastName;

    @JsonCreator
    public PersonMixIn(@JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName) {}
}

现在,Jackson将能够序列化我们的Person,并将序列化firstNamelastName

另一方面,如果我们还想使用Person类进行反序列化,我们需要在mixin类中添加一个jackson友好的构造函数。

测试并验证结果:

class MixinTest {
    private ObjectMapper mapper;
    
    @BeforeEach
    void beforeAll() {
        mapper = new ObjectMapper()
                // 启用换行
                .enable(SerializationFeature.INDENT_OUTPUT)
                .addMixIn(Person.class, PersonMixIn.class);
    }

    @Test
    @DisplayName("测试mixin")
    void mixin() throws IOException {
        Person bean = new Person("Zhao", "Liu");
        String result = mapper.writeValueAsString(bean);
        System.out.println(result);

        Person p =mapper.readerFor(Person.class).readValue(result);
        System.out.println(p);
    }
}

输出结果:

{
  "firstName" : "Zhao",
  "lastName" : "Liu"
}
Person(firstName=Zhao, lastName=Liu)

7、禁用Jackson注解

如何禁用所有Jackson注解?我们可以通过设置MapperFeature.USE_ANNOTATIONS来禁用实体类上的Jackson注解,创建如下实体类:

@JsonInclude(Include.NON_NULL)
@Getter
@ToString
public class IncludeUser {
    private int id;
    private String name;
    private int age;
    private String address;
}

测试并验证结果:

@Test
public void disablingAllAnnotations() throws JsonProcessingException {

    IncludeUser bean = new IncludeUser();

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(MapperFeature.USE_ANNOTATIONS);

    String result = mapper.writeValueAsString(bean);
    System.out.println(result);
}

禁用注解 之前输出结果:

{"id":0,"age":0}

禁用注解 之后输出结果:

{"id":0,"name":null,"age":0,"address":null}