Jackson注解大全(上)

发布时间 2023-12-28 10:41:46作者: 全琪俊

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 //@JsonAnyGetter
public 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":"成都"
}

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

1.2. @JsonGetter

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

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

public class UserWithGetter {

    private String name;
    private int age;

    public UserWithGetter() {

    }

    public UserWithGetter(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @JsonProperty("name")
    @JsonGetter("name")
    public String getTheName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

测试方法如下所示:

@Test //@JsonGetter
public void jonGetter() 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类,并自定义属性的顺序:

@JsonPropertyOrder({"name","age","id"})
public class User {

    private int id;
    private String name;
    private int age;
    //省略 constructor  getters setters
}

测试JSON输出:

@Test //@JsonPropertyOrder
public 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)按字母顺序对属性进行排序。在这种情况下,序列化输出结果是:

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

1.4. @JsonRawValue

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

定义测试实体类:

public class RawUser {

    private String name;

    @JsonRawValue
    private String address;

   //省略 constructor  getters setters
}

编写测试用例:

@Test //@JsonRawValue
public 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:

public class UserJsonValue {
    private String name;
    private int age;

    @JsonValue
    public String getName() {
        return name;
    }
   //省略 constructor  getters setters
}

编写测试用例测试:

@Test //@JsonValue
public void JsonValue() throws JsonProcessingException {
    UserJsonValue user = new UserJsonValue("John", 27);
    ObjectMapper mapper = new ObjectMapper();
    String result = mapper.writeValueAsString(user);
    System.out.println(result);
}

输出结果:

"John"

1.6. @JsonRootName

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

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

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

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

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

定义包装实体:

@JsonRootName(value = "user")
public class UserWithRoot {
    private int id;
    private String name;
    //省略 constructor  getters setters
}

编写测试用例进行测试:

@Test //@JsonRootName
public 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前缀赋予了一个与某个命名空间相关联的限定名称:

@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
    private int id;
    private String name;
    //省略 constructor  getters setters
}

序列化测试用例:

@Test //@JsonRootName namespace
public 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 StdSerializer<Date> {

    private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public CustomDateSerializer() {
        this(null);
    }

    public CustomDateSerializer(final Class<Date> t) {
        super(t);
    }

    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider ser) throws IOException {
        jsonGenerator.writeString(formatter.format(date));
    }
}

编写测试用例:

@Test
public void jsonSerialize() throws JsonProcessingException, ParseException {

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    String toParse = "2020-05-19 02:30:00";
    Date date = df.parse(toParse);
    Person person = new Person("Tom", date);

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

输出结果如下:

{
    "name":"Tom",
    "birthday":"2020-05-19 02:30:00"
}

2. Jackson反序列化注解

2.1. @JsonCreator

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

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

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

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

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

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;
    }
    //省略 constructor  getters setters
}

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

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

输出结果如下:

UserWithCreator{id=1, name='Jack'}

2.2. @JacksonInject

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

定义反序列化实体类:

public class UserWithInject {
    @JacksonInject
    private int id;
    private String name;
    @JacksonInject
    private String address;
     //省略 constructor  getters setters
}

定义测试用例:

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

输出结果如下:

UserWithInject{id=1, name='Jack', address='北京'}

2.3. @JsonAnySetter

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

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

@JsonAnySette注意事项:

  1. 此注解用在非静态方法上,注解的方法必须有两个参数,第一个是json字段中的key,第二个是value,方法名随意。

  2. 此注解也可以用在Map对象属性上面,建议用在Map对象属性上面。

  3. 反序列化的时候将对应不上的字段全部放到Map里面。

使用@JsonAnySetter反序列化实体ExtendableUser

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>();
    }

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

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

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

编写测试用例测试:

@Test
public void jsonAnySetter() throws IOException {
    String json = "{\"name\":\"Jack\",\"province\":\"四川\",\"city\":\"成都\"}";
    ObjectMapper mapper = new ObjectMapper();
    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方法:

public class UserWithGetter {

    private String name;
    private int age;

    @JsonProperty("name")
    @JsonSetter("name")
    public void setTheName( String name) {
        this.name = name;
    }
    //省略构造函数和其他方法
}

编写测试用例测试:

@Test
public void jsonSetter() throws IOException {
    String json = "{\"name\":\"Jack\",\"age\":28}";
    ObjectMapper mapper = new ObjectMapper();
    UserWithGetter user =mapper.readerFor(UserWithGetter.class).readValue(json);
    System.out.println(user);
}
//输出结果:UserWithGetter{name='Jack', age=28}

2.5. @JsonDeserialize

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

定义反序列化类Person:

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

自定义反序列化器:

public class CustomDateDeserializer extends StdDeserializer<Date> {
    private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(final Class<?> vc) {
        super(vc);
    }
    @Override
    public Date deserialize( JsonParser jsonparser,  DeserializationContext context) throws IOException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (final ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

测试代码如下:

@Test
public void jsonDeserialize() throws IOException {
    String json = "{\"name\":\"Jack\",\"birthday\":\"2020-05-20 02:30:00\"}";

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    ObjectMapper mapper = new ObjectMapper();
    Person person =mapper.readerFor(Person.class).readValue(json);
    System.out.println(person);
    System.out.println(df.format(person.getBirthday()));
}

输出结果如下:

Person{name='Jack', birthday=Wed May 20 02:30:00 CST 2020}
2020-05-20 02:30:00

2.6 @JsonAlias

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

定义AliasBean类来演示:

public class AliasUser {

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

    private String lastName;
    //省略 constructor  getters setters
}

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

测试用例代码如下:

@Test
public void jsonAlias() throws IOException {
    String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
    ObjectMapper mapper = new ObjectMapper();
    AliasUser aliasUser = mapper.readerFor(AliasUser.class).readValue(json);
    System.out.println(aliasUser);
}
//输出结果:AliasUser{firstName='John', lastName='Green'}

3. Jackson属性注解

3.1. @JsonIgnoreProperties

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

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

@JsonIgnoreProperties({ "id" })
public class UserWithIgnore {
    private int id;
    private String name;
    //省略 constructor  getters setters
}

编写测试代码:

@Test
public void jsonIgnoreProperties() throws JsonProcessingException {
    UserWithIgnore bean = new UserWithIgnore(1, "Jack");
    String result = new ObjectMapper().writeValueAsString(bean);
}

输出结果如下:

{"name":"Jack"}

3.2. @JsonIgnore

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

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

public class UserWithIgnore {
    @JsonIgnore
    private int id;
    private String name;
    //省略 constructor  getters setters
}

测试代码如下:

@Test
public void jsonIgnore() throws JsonProcessingException {
    UserWithIgnore user = new UserWithIgnore(1, "Jack");
    String result = new ObjectMapper().writeValueAsString(user);
    System.out.println(result);
}

输出结果如下:

{"name":"Jack"}

3.3. @JsonIgnoreType

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

public class UserWithIgnoreType {
    public int id;

    public Name name;

    public UserWithIgnoreType() {

    }

    public UserWithIgnoreType(final int id, final Name name) {
        this.id = id;
        this.name = name;
    }

    @JsonIgnoreType
    public static class Name {
        public String firstName;
        public String lastName;

        public Name() {

        }

        public Name(final String firstName, final String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
}

测试代码如下:

@Test
public void jsonIgnoreType() throws JsonProcessingException {
    UserWithIgnoreType.Name name = new UserWithIgnoreType.Name("John", "Doe");
    UserWithIgnoreType user = new UserWithIgnoreType(1, name);

    String result = new ObjectMapper().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}

3.5. @JsonAutoDetect

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

来看一个简单的示例:

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

    public PrivateUser() {

    }

    public PrivateUser(final int id, final String name) {
        this.id = id;
        this.name = name;
    }
}

测试代码如下:

@Test
public void jsonAutoDetect() throws JsonProcessingException {
    PrivateUser bean = new PrivateUser(1, "Jack");

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

输出结果:

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