自学内容网 自学内容网

Java处理JSON神器:Jackson使用及常用配置详细文档

文章目录

一、jackson简介

Java解析json的第三方jar包有很多,fastjson2、Gson、jackson等。
而Spring把jackson作为默认的json解析工具,还是有必要学习一下的。

jackson有着灵活的配置,但是使用起来比较复杂,远没有fastjson2、Gson简单。

github地址:https://github.com/FasterXML/jackson
比较全的官方文档:https://www.baeldung.com/jackson

项目中使用springboot-starter-web,就自动引入了jackson。

<!-- 该包会引入 jackson-core和jackson-annotations-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

二、jackson注解使用详解

1、序列化注解

(1)@JsonAnyGetter格式化Map

没标注前的结果:{"name":"zhangsan","properties":{"attr2":"val2","attr1":"val1"}}
标注后的结果:{"name":"zhangsan","attr2":"val2","attr1":"val1"}

注意,@JsonAnyGetter要标注在get方法上,如果标注在属性上,结果就会是这样:{"name":"zhangsan","properties":{"attr2":"val2","attr1":"val1"},"attr2":"val2","attr1":"val1"}

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class Student {
    public String name;

    private Map<String, String> properties;

    public Student(String name) {
        this.name = name;
    }


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


    public void add(String attr, String val) {
        if (this.properties == null) {
            this.properties = new HashMap<>();
        }
        properties.put(attr, val);
    }

    public static void main(String[] args) throws JsonProcessingException {
        Student bean = new Student("zhangsan");
        bean.add("attr1", "val1");
        bean.add("attr2", "val2");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result);
    }
}

(2)@JsonGetter设置get方法与字段名

@JsonGetter标注在get方法上,确定一个get方法,并且可以设置格式化的key的名称。

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;
    private String name;

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

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public static void main(String[] args) throws JsonProcessingException {
        Student bean = new Student(1, "zhangsan");
        String result = new ObjectMapper().writeValueAsString(bean);
        // {"id":1,"name":"zhangsan"}
        System.out.println(result);
    }
}

(3)@JsonPropertyOrder设置格式化顺序

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
// 指定格式化顺序
@JsonPropertyOrder({ "name", "id" })
public class Student {
    private int id;
    private String name;

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

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public static void main(String[] args) throws JsonProcessingException {
        Student bean = new Student(1, "zhangsan");
        String result = new ObjectMapper().writeValueAsString(bean);
        // {"name":"zhangsan","id":1}
        System.out.println(result);
    }
}

(4)@JsonRawValue格式化字符串

import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;

    //@JsonRawValue
    private String name;

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

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public static void main(String[] args) throws JsonProcessingException {
        Student bean = new Student(1, "{\"attr\":\"zhangsan\"}");
        String result = new ObjectMapper().writeValueAsString(bean);
        // 不用@JsonRawValue:{"id":1,"name":"{\"attr\":\"zhangsan\"}"}
        // 用@JsonRawValue:{"id":1,"name":{"attr":"zhangsan"}}
        System.out.println(result);
    }
}

(5)@JsonValue指定格式化方法

使用@JsonValue标注的get方法,会作为该对象的格式化方法,该方法的返回值就是格式化后的值。

import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;

    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    @JsonValue
    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public static void main(String[] args) throws JsonProcessingException {
        Student bean = new Student(1, "zhangsan");
        String result = new ObjectMapper().writeValueAsString(bean);
        // "zhangsan"
        System.out.println(result);
    }
}

(6)@JsonRootName对根进行包装

对该对象最外层进行包装

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

@JsonRootName(value = "student")
public class Student {
    private int id;

    private String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // 需要开启根包装

        Student bean = new Student(1, "zhangsan");
        String result = mapper.writeValueAsString(bean);
        // {"student":{"id":1,"name":"zhangsan"}}
        System.out.println(result);
    }
}

(7)@JsonSerialize指定字段序列化器

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Student {
    private int id;

    private String name;

// 指定序列化器
    @JsonSerialize(using = CustomDateSerializer.class)
    private Date birthday;

    public Student(int id, String name, Date birthday) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        Student bean = new Student(1, "zhangsan", new Date());
        String result = mapper.writeValueAsString(bean);
        // {"id":1,"name":"zhangsan","birthday":"2024-11-18 04:01:40"}
        System.out.println(result);
    }


// 需要定义序列化类
    public static class CustomDateSerializer extends StdSerializer<Date> {

        private static SimpleDateFormat formatter
                = new SimpleDateFormat("yyyy-MM-dd 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));
        }
    }
}

2、反序列化注解

(1)@JsonCreator与@JsonProperty指定反序列构造器与字段名

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;

    private String name;
// 指定构造器和字段名
    @JsonCreator
    public Student(@JsonProperty("id")int id, @JsonProperty("theName")String name) {
        this.id = id;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1,\"theName\":\"zhangsan\"}";
        Student bean = mapper.readValue(json, Student.class);
        System.out.println(bean);
    }
}

(2)@JacksonInject注入字段数据

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {

    // 指定注入字段
    @JacksonInject
    private int id;

    private String name;

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"name\":\"zhangsan\"}";
        InjectableValues inject = new InjectableValues.Std()
                .addValue(int.class, 1); // 将int类型注入为1
        Student bean = mapper.reader(inject).forType(Student.class).readValue(json);
        System.out.println(bean);
    }
}

(3)@JsonAnySetter写入map

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class Student {

    private String name;

    private Map<String, String> properties;

    public String getName() {
        return name;
    }

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

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

    public void setProperties(Map<String, String> properties) {
        this.properties = properties;
    }

// 只能这样写,不能直接放在set方法上
    @JsonAnySetter
    public void add(String key, String value) {
        if (properties == null) {
            properties = new HashMap<>();
        }
        properties.put(key, value);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", properties=" + properties +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"name\":\"zhangsan\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
        Student bean = mapper.readValue(json, Student.class);
        // Student{name='zhangsan', properties={attr2=val2, attr1=val1}}
        System.out.println(bean);
    }
}

(4)@JsonSetter指定字段名

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    // 指定字段名
    @JsonSetter("theName")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1,\"theName\":\"zhangsan\"}";
        Student bean = mapper.readValue(json, Student.class);
        System.out.println(bean);
    }
}

(5)@JsonDeserialize指定字段反序列化器

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Student {
    private int id;

    private String name;

    // 指定反序列化器
    @JsonDeserialize(using = CustomDateDeserializer.class)
    private Date birthday;

    public Student() {
    }

    public Student(int id, String name, Date birthday) {
        this.id = id;
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1,\"name\":\"zhangsan\",\"birthday\":\"2024-11-18 04:01:40\"}";
        Student result = mapper.readValue(json, Student.class);
        // Student{id=1, name='zhangsan', birthday=Mon Nov 18 04:01:40 GMT+08:00 2024}
        System.out.println(result);
    }


    // 需要定义反序列化类
    public static class CustomDateDeserializer extends StdDeserializer<Date> {

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

        public CustomDateDeserializer() {
            this(null);
        }

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

        @Override
        public Date deserialize(
                JsonParser jsonparser, DeserializationContext context)
                throws IOException {

            String date = jsonparser.getText();
            try {
                return formatter.parse(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

(6)@JsonAlias为字段指定多个别名

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Student {
    private int id;

    // 指定多个别名
    @JsonAlias({ "fname", "f_name" })
    private String name;



    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1,\"fname\":\"zhangsan\"}";
        Student result = mapper.readValue(json, Student.class);
        System.out.println(result);
    }
}

3、其他注解

(1)@JsonIgnoreProperties与@JsonIgnore字段忽略

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// 类级别,忽略id字段,序列化和反序列化都会忽略
@JsonIgnoreProperties({ "id" })
public class Student {

    // 字段级别,忽略id字段,只序列化,反序列化会忽略
    @JsonIgnore
    private int id;

    private String name;

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1,\"name\":\"zhangsan\"}";
        Student result = mapper.readValue(json, Student.class);
        System.out.println(result);

        System.out.println(mapper.writeValueAsString(result));
    }
}

(2)@JsonInclude排除null字段

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// null字段不参与序列化
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Student {

    private Integer id;

    private String name;

    public String getName() {
        return name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"name\":\"zhangsan\"}";
        Student result = mapper.readValue(json, Student.class);
        System.out.println(result);

        System.out.println(mapper.writeValueAsString(result));
    }
}

(3)@JsonIncludeProperties指定序列化的字段

import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

// 指定序列化的字段
@JsonIncludeProperties({ "name" })
public class Student {

    private Integer id;

    private String name;

    public String getName() {
        return name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = "{\"id\":1, \"name\":\"zhangsan\"}";
        Student result = mapper.readValue(json, Student.class);
        System.out.println(result);

        System.out.println(mapper.writeValueAsString(result));
    }
}

(4)@JsonIgnoreType忽略类型

public class User {
    public int id;
    public Name name;

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


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

    String result = new ObjectMapper()
      .writeValueAsString(user);

    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
    assertThat(result, not(containsString("John")));
}

(5)@JsonAutoDetect指定属性是否可见

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


@Test
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"));
}

(6)@JsonProperty指定字段名

public class MyBean {
    public int id;
    private String name;

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

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

@Test
public void whenUsingJsonProperty_thenCorrect()
  throws IOException {
    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));

    MyBean resultBean = new ObjectMapper()
      .readerFor(MyBean.class)
      .readValue(result);
    assertEquals("My bean", resultBean.getTheName());
}

(7)@JsonFormat指定时间/日期格式

public class EventWithFormat {
    public String name;

    @JsonFormat(
      shape = JsonFormat.Shape.STRING,
      pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

(8)@JsonUnwrapped指定展开/平铺的值

public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    public static class Name {
        public String firstName;
        public String lastName;
    }
}

@Test
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"
}

*/

(9)@JsonView指示将在其中包含属性以进行序列化/反序列化的视图

// 视图
public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}

// 使用视图
public class Item {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Internal.class)
    public String ownerName;
}

// 测试
@Test
public void whenSerializingUsingJsonView_thenCorrect()
  throws JsonProcessingException {
    Item item = new Item(2, "book", "John");

    String result = new ObjectMapper()
      .writerWithView(Views.Public.class)
      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("2"));
    assertThat(result, not(containsString("John")));
}

(10)@JsonManagedReference、@JsonBackReference处理循环引用

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

@Test
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);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}

(11)@JsonIdentityInfo处理无限递归问题

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

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

@Test
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);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));
}

// 结果
{
    "id": 2,
    "itemName": "book",
    "owner": {
        "id": 1,
        "name": "John",
        "userItems": [
            2
        ]
    }
}

(12)@JsonFilter指定过滤器

@JsonFilter("myFilter")
public class BeanWithFilter {
    public int id;
    public String name;
}

// 我们定义了过滤器,它排除了除名字来自序列化:
@Test
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);

    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

4、处理多态注解

@JsonTypeInfo–指示序列化中要包含的类型信息的详细信息
@JsonSubTypes–指示注释类型的子类型
@JsonTypeName–定义用于注释类的逻辑类型名

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Zoo {
    public Animal animal;

    @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 static class Animal {
        public String name;

        public Animal(String name) {
            this.name = name;
        }
    }

    @JsonTypeName("dog")
    public static class Dog extends Animal {
        public double barkVolume;

        public Dog(String name) {
            super(name);
        }
    }

    @JsonTypeName("cat")
    public static class Cat extends Animal {
        boolean likesCream;
        public int lives;

        public Cat(String name) {
            super(name);
        }
    }

    public Zoo(Animal animal) {
        this.animal = animal;
    }

    public static void main(String[] args) throws JsonProcessingException {
        Zoo.Dog dog = new Zoo.Dog("lacy");
        Zoo zoo = new Zoo(dog);

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

5、自定义jackson注解

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

// 使用
@CustomAnnotation
public class BeanWithCustomAnnotation {
    public int id;
    public String name;
    public Date dateCreated;
}

// 多个注解合一
@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
  throws JsonProcessingException {
    BeanWithCustomAnnotation bean 
      = new BeanWithCustomAnnotation(1, "My bean", null);

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("dateCreated")));
}

// 结果
{
    "name":"My bean",
    "id":1
}

6、禁用jackson注解

@Test
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);
    
    assertThat(result, containsString("1"));
    assertThat(result, containsString("name"));
}

三、使用ObjectMapper

public class Car {

    private String color;
    private String type;

    // standard getters setters
}

1、序列化

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
// 序列化到文件 :{"color":"yellow","type":"renault"}
objectMapper.writeValue(new File("target/car.json"), car);
// 序列化到字符串
String carAsString = objectMapper.writeValueAsString(car);

2、反序列化

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
// 从字符串
Car car = objectMapper.readValue(json, Car.class);
// 从文件
Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);
// 从url
Car car = objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);

3、强大的JsonNode

可以将JSON解析成JsonNode对象,用于从特定节点检索数据:

String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
String color = jsonNode.get("color").asText();
// Output: color -> Black

4、反序列化集合

String jsonCarArray = 
  "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
  // 用类型引用
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});

// 反序列化为数组
String jsonCarArray = 
  "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class);
// print cars

5、反序列化Map

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map 
  = objectMapper.readValue(json, new TypeReference<Map<String,Object>>(){});

6、映射没有的字段,防止报错

ObjectMapper objectMapper = new ObjectMapper();
String jsonString
        = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";

// 映射没有的字段,防止报错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Car car = objectMapper.readValue(jsonString, Car.class);

JsonNode jsonNodeRoot = objectMapper.readTree(jsonString);
JsonNode jsonNodeYear = jsonNodeRoot.get("year");
String year = jsonNodeYear.asText();
System.out.println(year);

7、自定义序列化/反序列化器

public class CustomCarSerializer extends StdSerializer<Car> {
    
    public CustomCarSerializer() {
        this(null);
    }

    public CustomCarSerializer(Class<Car> t) {
        super(t);
    }

    @Override
    public void serialize(
      Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("car_brand", car.getType());
        jsonGenerator.writeEndObject();
    }
}

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = 
  new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null));
module.addSerializer(Car.class, new CustomCarSerializer());
mapper.registerModule(module);
Car car = new Car("yellow", "renault");
String carJson = mapper.writeValueAsString(car);


public class CustomCarDeserializer extends StdDeserializer<Car> {
    
    public CustomCarDeserializer() {
        this(null);
    }

    public CustomCarDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Car deserialize(JsonParser parser, DeserializationContext deserializer) {
        Car car = new Car();
        ObjectCodec codec = parser.getCodec();
        JsonNode node = codec.readTree(parser);
        
        // try catch block
        JsonNode colorNode = node.get("color");
        String color = colorNode.asText();
        car.setColor(color);
        return car;
    }
}

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module =
  new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Car.class, new CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);


8、处理Date类型

public class Request 
{
    private Car car;
    private Date datePurchased;

    // standard getters setters
}

ObjectMapper objectMapper = new ObjectMapper();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
objectMapper.setDateFormat(df); // 指定Date格式化
String carAsString = objectMapper.writeValueAsString(request);
// output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"}

9、用建造者模式创建ObjectMapper

ObjectMapper mapper = new ObjectMapperBuilder()
  .enableIndentation()
  .dateFormat()
  .preserveOrder(true)
  .build();

四、处理未知属性的JSON内容

1、引出问题

假设有以下实体类:

public class MyDto {

    private String stringValue;
    private int intValue;
    private boolean booleanValue;

    // standard constructor, getters and setters 
}

解析未知属性,会报错com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException

@Test(expected = UnrecognizedPropertyException.class)
public void givenJsonHasUnknownValues_whenDeserializingAJsonToAClass_thenExceptionIsThrown()
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString =
        "{\"stringValue\":\"a\"," +
        "\"intValue\":1," +
        "\"booleanValue\":true," +
        "\"stringValue2\":\"something\"}";
    ObjectMapper mapper = new ObjectMapper();

    MyDto readValue = mapper.readValue(jsonAsString, MyDto.class);

    assertNotNull(readValue);
}

// 以下异常:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: 
Unrecognized field "stringValue2" (class org.baeldung.jackson.ignore.MyDto), 
not marked as ignorable (3 known properties: "stringValue", "booleanValue", "intValue"])

2、解决方案一

// 忽略未知属性
new ObjectMapper()
  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

3、解决方案二

// 忽略未知属性
@JsonIgnoreProperties(ignoreUnknown = true)
public class MyDtoIgnoreUnknown { ... }

五、常见异常与处理方案

1、JsonMappingException: Can Not Construct Instance Of

public class Zoo {
    public Animal animal;
    
    public Zoo() { }
}

abstract class Animal {
    public String name;
    
    public Animal() { }
}

class Cat extends Animal {
    public int lives;
    
    public Cat() { }
}


@Test(expected = JsonMappingException.class)
public void givenAbstractClass_whenDeserializing_thenException() 
  throws IOException {
    String json = "{"animal":{"name":"lacy"}}";
    ObjectMapper mapper = new ObjectMapper();
// JsonMappingException:无法构造实例
    mapper.reader().forType(Zoo.class).readValue(json);
}

// 以下是异常信息
com.fasterxml.jackson.databind.JsonMappingException: 
Can not construct instance of org.baeldung.jackson.exception.Animal,
  problem: abstract types either need to be mapped to concrete types, 
  have custom deserializer, 
  or be instantiated with additional type information
  at 
[Source: {"animal":{"name":"lacy"}}; line: 1, column: 2] 
(through reference chain: org.baeldung.jackson.exception.Zoo["animal"])
at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

解决方案:

// 指定子类类型
@JsonDeserialize(as = Cat.class)
abstract class Animal {...}

2、JsonMappingException: No Suitable Constructor

public class User {
    public int id;
    public String name;

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

@Test(expected = JsonMappingException.class)
public void givenNoDefaultConstructor_whenDeserializing_thenException() 
  throws IOException {
    String json = "{"id":1,"name":"John"}";
    ObjectMapper mapper = new ObjectMapper();

    mapper.reader().forType(User.class).readValue(json);
}

// 报错信息
com.fasterxml.jackson.databind.JsonMappingException: 
No suitable constructor found for type 
[simple type, class org.baeldung.jackson.exception.User]:
 can not instantiate from JSON object (need to add/enable type information?)
 at [Source: {"id":1,"name":"John"}; line: 1, column: 2]
        at c.f.j.d.JsonMappingException.from(JsonMappingException.java:148)

解决方案:需要添加一个无参构造器

3、JsonMappingException: Root Name Does Not Match Expected

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

@Test
public void 
  givenWrappedJsonStringAndConfigureClass_whenDeserializing_thenCorrect() 
  throws IOException {
 
    String json = "{"user":{"id":1,"name":"John"}}";

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);

    UserWithRoot user = mapper.reader()
      .forType(UserWithRoot.class)
      .readValue(json);
    assertEquals("John", user.name);
}

如果有@JsonRootName,序列化和反序列化时需要注意root。

4、JsonMappingException: No Serializer Found for Class

如果实体类的属性或者get、set方法是私有的,就会报这个错。

解决方案:

@Test
public void givenClassWithPrivateFields_whenConfigureSerializing_thenCorrect() 
  throws IOException {
 
    UserWithPrivateFields user = new UserWithPrivateFields(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); // 解决方案一:配置能见度

    String result = mapper.writer().writeValueAsString(user);
    assertThat(result, containsString("John"));
}

// 解决方案二:使用@JsonAutoDetect
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class UserWithPrivateFields { ... }

// 解决方案三:使用public的get、set方法

5、JsonMappingException: Can Not Deserialize Instance Of

注意数组和对象关系

@Test
public void givenJsonOfArray_whenDeserializing_thenCorrect() 
  throws JsonProcessingException, IOException {
 
    String json
      = "[{"id":1,"name":"John"},{"id":2,"name":"Adam"}]";
   
    ObjectMapper mapper = new ObjectMapper();
    List<User> users = mapper.reader()
      .forType(new TypeReference<List<User>>() {})
      .readValue(json);

    assertEquals(2, users.size());
}

6、JsonMappingException: Cannot Deserialize Value of Type java.lang.String From Object Value

注意序列化字段的类型。

7、UnrecognizedPropertyException

看第四章,处理未知属性的JSON内容。

8、JsonParseException: Unexpected Character (”’ (code 39))

jackson默认不支持单引号,需要手动开启

@Test
public void 
  givenStringWithSingleQuotes_whenConfigureDeserializing_thenCorrect() 
  throws JsonProcessingException, IOException {
 
    String json = "{'id':1,'name':'John'}";

    JsonFactory factory = new JsonFactory();
    factory.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); // 支持单引号
    ObjectMapper mapper = new ObjectMapper(factory);

    User user = mapper.reader().forType(User.class)
      .readValue(json);
 
    assertEquals("John", user.name);
}

9、JsonParseException: Unexpected Character (‘c’ (code n))

检查json字符串格式是否正确。

10、MismatchedInputException: Cannot Deserialize Instance

需要使用默认无参构造器


原文地址:https://blog.csdn.net/A_art_xiang/article/details/143855746

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!