Jackson详解

7 阅读22分钟

一,Jackjson介绍

Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。

  • 序列化:将 Java Bean 转换为JSON 字符串
  • 反序列化:将JSON字符串转换为JavaBeen对象

最主要的是SpringBoot默认使用的序列化与反序列化工具就是Jackjson

主要功能

  1. 高性能:Jackson 是一个高性能的 JSON 处理库,序列化和反序列化的速度都很快。
  2. 简单易用:使用 Jackson 序列化和反序列化只需添加少量代码。
  3. 灵活性强:Jackson 支持各种类型的 JSON 数据结构,并且可以通过注解和配置进行自定义。
  4. 支持多种数据格式:除了 JSON,Jackson 还支持 XML、YAML、CSV 等多种数据格式。

Jackson库包括三个主要的模块

  • jackson-databind:用于数据绑定
  • jackson-core:用于JSON解析和生成
  • jackson-annotations:用于注解支持

二,Jackjson快速入门

  1. 创建一个User对象

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User  {
        private Long id;
    
        private String name;
    
        private String pwd;
    
        private Long rid;
    }
    
  2. 序列化

        @Autowired
        ObjectMapper objectMapper;
        @Test
        public void testJson() throws JsonProcessingException {
            User user = new User(1L,"张三","123456",1L);
            String userstr = objectMapper.writeValueAsString(user);
            System.out.println(userstr);
        }
    
  3. 反序列化

        @Autowired
        ObjectMapper objectMapper;
        @Test
        public void testJson2() throws JsonProcessingException {
            String userStr ="{\"name\":\"小凡\",\"pwd\":18}";
            User user = objectMapper.readValue(userStr, User.class);
            System.out.println(user);
        }
    

三,Jackjson序列化API

序列化只需要一个方法:

  • public String writeValueAsString(Object value):将Java对象序列化

3.1 普通Java对象序列化

@Test
public void testObjectToJson() throws JsonProcessingException {
    User user = new User();
    user.setName("小凡");
    user.setAge(18);
    ObjectMapper mapper = new ObjectMapper();
    String userstr = mapper.writeValueAsString(user);
    System.out.println(userstr);
}
//输出
{"name":"小凡","age":18}

3.2 嵌套Java对象序列化

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Long id;

    private String name;

    private String pwd;
    
    private Long rid;

    private Book book;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private String bookName;
    private Double price;
}
    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws JsonProcessingException {
        User user = new User(1L,"张三","123456",1L);
        Book book = new Book("java", 100.0);
        user.setBook(book);
        String userStr = objectMapper.writeValueAsString(user);
        System.out.println(userStr);
    }

3.3 序列化List集合

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws JsonProcessingException {
        User user1 = new User(1L,"张三","123456",1L);
        Book book1 = new Book("java", 100.0);
        user1.setBook(book1);
        User user2 = new User(1L,"张三","123456",1L);
        Book book2 = new Book("java", 100.0);
        user2.setBook(book2);

        List<User> userList = new ArrayList<>();
        userList.add(user1);
        userList.add(user2);

        String userStr = objectMapper.writeValueAsString(userList);
        System.out.println(userStr);
    }

3.4 序列化Map集合

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws JsonProcessingException {
        User user1 = new User(1L,"张三","123456",1L);
        Book book1 = new Book("java", 100.0);
        user1.setBook(book1);
        User user2 = new User(1L,"张三","123456",1L);
        Book book2 = new Book("java", 100.0);
        user2.setBook(book2);

        Map<String, User> userMap=new HashMap<>();
        userMap.put("user1",user1);
        userMap.put("user2",user2);

        String userStr = objectMapper.writeValueAsString(userMap);
        System.out.println(userStr);
    }

3.5 对日期处理

默认情况下,jackjson会将日期类型属性序列化成long型值(自1970年1月1日以来的毫秒数)。显然这样格式的数据不符合人类直观查看

  1. 创建一个Person对象

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class Person {
        private String name;
        private Date birthday;
    }
    
  2. 默认转化结果

        @Test
        public void testJson() throws JsonProcessingException {
            Person person = new Person("张三", new Date());
            ObjectMapper objectMapper=new ObjectMapper();
            String personStr = objectMapper.writeValueAsString(person);
            System.out.println(personStr);
            //{"name":"张三","birthday":1721958095622}
        }
    

坑点:

  • 不支持jdk8的时间类的转换
  • 时间需要手动格式化

解决方法SpringBoot时间格式化问题 - wdadwa - 博客园 (cnblogs.com)

四,Jackjson反序列化API

Jackson通过将JSON字段的名称与Java对象中的getter和setter方法进行匹配,将JSON对象的字段映射到Java对象中的属性,Jackson删除了getter和setter方法名称的“ get”和“ set”部分,并将其余名称的第一个字符转换为小写。

4.1 反序列化为Java对象

针对普通Json字符串反序列化执行使用

  • readValue(src,valueType):方法即可实现

4.1.1 JSON字符串

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson2() throws JsonProcessingException {
        String userStr ="{\"name\":\"小凡\",\"pwd\":18}";
        User user = objectMapper.readValue(userStr, User.class);
        System.out.println(user);
    }

4.1.2 字符输入流

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        String json ="{\"name\":\"小医仙\",\"age\":18}";
        Reader reader = new StringReader(json);
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(reader, User.class);
        System.out.println(user);
    }

4.1.3 字节输入流

  1. 创建user.json文件,文件内容如下
{
    "name":"张三",
    "age": 28
}
  1. 将字节输入流转换为User对象

        @Autowired
        ObjectMapper objectMapper;
        @Test
        public void testJson() throws IOException {
            FileInputStream fis = new FileInputStream("./src/main/resources/user.json");
            ObjectMapper mapper = new ObjectMapper();
            User user = mapper.readValue(fis, User.class);
            System.out.println(user);
        }
    

4.1.4 File文件

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        File file = new File("./src/main/resources/user.json");
        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.readValue(file, User.class);
        System.out.println(user);
    }

4.1.5 URL文件

@Autowired
ObjectMapper objectMapper;
@Test
public void testUrlToObject() throws IOException {
    String url ="https://files.cnblogs.com/files/blogs/685650/user.json";
    URL url1 = new URL(url);
    User user = mapper.readValue(url1, User.class);
    System.out.println(user);
}

4.1.6 字节数组

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        String json ="{\"name\":\"韩雪\",\"age\":18}";
        byte[] bytes = json.getBytes();
        User user = objectMapper.readValue(bytes, User.class);
        System.out.println(user);
    }

4.2 反序列化为集合

反序列化集合,readValue的第二个参数就要从Class对象改为TypeReference了

  • TypeReference<T>:T里面就是我们要转化的那个类型,例如
    • TypeReference<Map<String,Object>>
    • TypeReference< List<User> >
  • 如果是普通对象数组就是
    • User[].class

4.2.1 Map集合

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        //{"name":"小凡","sex":"男","age":18}
        String json ="{\"name\":\"小凡\",\"sex\":\"男\",\"age\":18}";
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {});
        for (String key : map.keySet()) {
            System.out.println(key+":"+map.get(key));
        }
    }

4.2.2 List集合


    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        //{"name":"小凡","sex":"男","age":18}
        String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";
        List<User> users = objectMapper.readValue(json, new TypeReference<List<User>>() {
        });
        System.out.println(users);
    }

4.2.3 数组

    @Autowired
    ObjectMapper objectMapper;
    @Test
    public void testJson() throws IOException {
        //{"name":"小凡","sex":"男","age":18}
        String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";
        User[] users = objectMapper.readValue(json, User[].class);
        for (User user : users) {
            System.out.println(user);
        }
    }

五,自定义序列化与反序列化器

5.1 自定义序列化器

有时候,我们不需要jackjson默认的序列化方式。例如,JSON中使用与Java对象中不同的属性名称, 或者不需要Java对象中的某个字段,这时我们就需要自定义序列化器

  1. User实体对象定义如下

    @Data
    public class User {
        private String name;
        private Integer age;
    }
    
  2. 按照下面默认序列化代码,我们最后得到的JSON字符串如下

    @Test
    public void testSerializationExample() throws JsonProcessingException {
        User user = new User("小凡", 18);
        ObjectMapper objectMapper = new ObjectMapper();
        String userstr = objectMapper.writeValueAsString(user);
        System.out.println(userstr);
    }
    //输出
    {"name":"小凡","age":18}
    
  3. 而现在的需求升级了,再不创建或修改Users实体对象的情况下,我们想要得到{"username":"小凡","userage":18} 这样的字符串,这时,我们就需要自定义序列化器,就可以轻松实现,具体代码如下

        @Autowired
        ObjectMapper objectMapper;
        @Test
        public void testDefineSerialize() throws JsonProcessingException {
    
            SimpleModule version1Module = new SimpleModule();
            version1Module.addSerializer(User.class, new JsonSerializer<User>() {
                @Override
                public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                    jsonGenerator.writeStartObject();
                    jsonGenerator.writeStringField("userName", user.getName());
                    jsonGenerator.writeNumberField("userAge", user.getAge());
                    jsonGenerator.writeEndObject();
                }
            });
            objectMapper.registerModule(version1Module);
    
            User user = new User("小凡", 18);
            String json = objectMapper.writeValueAsString(user); // 使用版本1的序列化器
            System.out.println( json);
    
        }
    

5.2 自定义反序列化器

  1. 自定义一个反序列化器

    public class UserDeserializer extends StdDeserializer<User> {
    
        public UserDeserializer() {
            this(null);
        }
    
        public UserDeserializer(Class<?> vc) {
            super(vc);
        }
    
        @Override
        public User deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);
            String name = node.get("userName").asText();
            int age = (Integer) node.get("userage").numberValue();
    
            return new User(name, age);
        }
    }
    
  2. 将JSON字符串反序列化为 User 对象

        @Test
        public void testUserDeserializer() throws JsonProcessingException {
            ObjectMapper objectMapper = new ObjectMapper();
            SimpleModule module = new SimpleModule();
            module.addDeserializer(User.class, new UserDeserializer());
            objectMapper.registerModule(module);
    
            String json = "{\"userName\":\"小凡\",\"userage\":30}";
            User user = objectMapper.readValue(json, User.class);
    
            System.out.println(user);
        }
    

六,树模型

Jackson具有内置的树模型,可用于表示JSON对象。Jackson树模型由JsonNode类表示。

在处理JSON时,我们有时并不直接关心或无法直接映射到特定的Java对象,而是需要对JSON内容进行动态、灵活的操作。

这时,树模型就派上了用场。我们可以遍历、查询、更新或合并JSON数据,而无需预先定义对应的Java类。

6.1 JsonNode类型概览

JsonNode家族包括以下主要子类型:

  • ObjectNode:对应JSON对象,包含一组键值对,可以通过.put(key, value)添加或更新属性。
  • ArrayNode:对应JSON数组,包含一系列元素,可通过.add(value)插入新元素。
  • TextNodeIntNodeLongNodeDoubleNode等:分别对应JSON中的字符串、整数、长整数、浮点数等基本类型值。
  • BooleanNodeNullNode:分别对应JSON中的布尔值和null值。

6.2 创建与操作JsonNode实例

6.2.1 创建简单的JsonNode

    @Test
    public void testJackJsonTreeModelExample(){
        // 创建 ObjectMapper 实例
        ObjectMapper mapper = new ObjectMapper();

        // 创建并初始化各类型 JsonNode 实例
        ObjectNode personNode = mapper.createObjectNode();
        personNode.put("name", "小凡");
        personNode.put("age", 18);

        ArrayNode hobbiesNode = mapper.createArrayNode();
        hobbiesNode.add("写代码").add("看书").add("打豆豆");

        personNode.set("hobbies", hobbiesNode);
        // 输出构建的 JSON 字符串
        System.out.println(personNode);
    }
  1. 创建了一个ObjectMapper实例,它是JackJson的核心工具类,负责序列化和反序列化工作。
  2. 我们创建了一个ObjectNode代表JSON对象,设置了"name"和"age"两个属性。
  3. 创建了一个ArrayNode存储爱好列表,并将其添加到personNode中。
  4. 打印输出构建的JSON字符串。

6.2.2 从JSON字符串反序列化为JsonNode

    @Test
    public void testJackJsonTreeModelExample2() throws JsonProcessingException {
        String jsonInput = "{\"name\":\"小凡\",\"age\":18,\"hobbies\":[\"写代码\",\"看书\",\"打豆豆\"]}";

        ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(jsonInput);
        System.out.println(rootNode.get("name").asText());
        System.out.println(rootNode.get("age").asInt());
        System.out.println(rootNode.get("hobbies").get(0).asText());
        System.out.println(rootNode.get("hobbies").get(1).asText());
        System.out.println(rootNode.get("hobbies").get(2).asText());
    }
  1. 定义了一个JSON字符串。
  2. 使用ObjectMapperreadTree()方法将其反序列化为JsonNode。
  3. 通过调用.get(key)方法访问对象属性或数组元素,并使用.asText().asInt()等方法获取其值。

6.2.3 利用树模型进行深度查询、修改、合并等操作

   @Test
    public void testJsonManipulationExample() throws JsonProcessingException {
        String JSON_STRING = "{\"name\":\"小凡\",\"age\":18,\"hobbies\":[\"写代码\",\"看书\",\"打豆豆\"]}";
        ObjectMapper mapper = new ObjectMapper();

        // 将JSON字符串转换为JsonNode对象
        JsonNode rootNode = mapper.readTree(JSON_STRING);

        // **深度查询**
        // 查询"年龄"
        int age = rootNode.get("age").asInt();
        System.out.println("Age: " + age);

        // 查询第一个兴趣爱好
        String firstHobby = rootNode.get("hobbies").get(0).asText();
        System.out.println("First hobby: " + firstHobby);

        // **修改**
        // 修改年龄为20
        ((ObjectNode) rootNode).put("age", 20);
        // 添加新的兴趣爱好:"旅行"
        ((ArrayNode) rootNode.get("hobbies")).add("旅行");

        // **合并**
        // 假设有一个新的JSON片段要与原数据合并
        String additionalJson = "{\"address\":\"北京市\",\"job\":\"程序员\"}";
        JsonNode additionalNode = mapper.readTree(additionalJson);

        // 使用ObjectNode#setAll方法将新节点中的键值对合并到原始节点中
        ((ObjectNode) rootNode).setAll((ObjectNode) additionalNode);

        // 打印更新后的JSON字符串
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode));
    }

七,Jackson注解

7.1 通用注解

7.1.1 @JsonIgnore

@JsonIgnore 用于在序列化或反序列化过程中忽略某个属性

  1. 定义实体类Person
public class Person {
    private String name;
    @JsonIgnore // 添加此注解以忽略 password 字段
    private String password;
    // 构造器、getter/setter 方法等...
}
  1. 使用示例

    @Test
    public void testJsonIgnore() throws Exception{
        Person person = new Person("小凡", "123456");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(person);
    }
    //输出
    {
      "name": "小凡"
    }
    
  2. 详细解释

  • @JsonIgnore 注解直接放在属性声明前,如 private String password; 行上方。
  • Person 类的对象被序列化为 JSON 时,password 属性将不会出现在生成的 JSON 字符串中
  • 同样地,在反序列化过程中,如果 JSON 数据中包含 password 字段,Jackson 在解析时会自动忽略它,不会尝试将其值设置到对应的 Person 对象属性上。

7.1.2 @JsonIgnoreProperties

@JsonIgnore 注解相比,@JsonIgnoreProperties 用于批量指定在序列化或反序列化过程中应忽略的属性列表,特别适用于应对不明确或动态变化的输入 JSON 中可能存在但不应处理的额外字段

  1. 定义实体类Person

    @JsonIgnoreProperties({"password", "socialSecurityNumber"})
    public class Person {
        private String name;
        private String password;
        private String socialSecurityNumber;
    
        // 构造器、getter/setter 方法等...
    }
    
  2. 使用示例

@Test
public void testJsonIgnoreProperties() throws Exception{
    Person person = new Person("小凡", "123456","233535");
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(person);
}
//输出
{
  "name": "小凡"
}
  1. 详细解释

    • @JsonIgnoreProperties 注解放置在类定义的开始处,作为类级别的注解
    • 注解内通过一个字符串数组参数列举出要忽略的属性名。在这个例子中,{"password", "socialSecurityNumber"} 指定了 passwordsocialSecurityNumber 两个属性在序列化和反序列化时应被忽略。
    • 序列化 Person 对象时,生成的 JSON 字符串将不包含 passwordsocialSecurityNumber 属性。与前面 @JsonIgnore 示例类似,JSON 输出仅包含未被忽略的 name 属性
    • 反序列化时,即使输入的 JSON 数据中包含了 passwordsocialSecurityNumber 字段,Jackson 也会忽略它们,不会尝试将这些字段的值填充到相应的 Person 对象属性中。
  2. 额外功能

    @JsonIgnoreProperties有个可选的属性 ignoreUnknown,用于控制是否忽略 JSON 中存在但 Java 类中没有对应的未知属性。若设置为 true,则在反序列化时遇到未知属性时会自动忽略,避免抛出异常

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class Person {
        // ...
    }
    

7.1.3 @JsonIgnoreType

@JsonIgnoreType 注解在 Jackson 库中用于指示整个类在序列化或反序列化过程中应当被忽略。这适用于那些由于某种原因(如敏感信息、内部细节等)不需要或不应该被 JSON 化处理的类

  1. 定义CreditCardDetails 实体类

    // 标记该类在序列化和反序列化时应被忽略
    @JsonIgnoreType
    public class CreditCardDetails {
        private String cardNumber;
        private String cvv;
        private String expirationDate;
         // 构造器、getter/setter 方法等...
    }
    
  2. 定义Customer

    public class Customer {
        private String customerId;
        private String name;
        private CreditCardDetails creditCardDetails;
    
        // 构造器、getter/setter 方法等...
    }
    
  3. 使用示例

    public void testJsonIgnoreType() throws JsonProcessingException {
        Customer customer = new Customer("CUST001", "John Doe", new CreditCardDetails("1234567890123456", "123", "2024-12"));
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(customer);
    }
    //输出
    {
      "customerId": "CUST001",
      "name": "John Doe"
    }
    
  4. 详细解释

    • @JsonIgnoreType 注解放置在需要忽略的类定义前,作为类级别的注解。
    • Customer 类对象被序列化为 JSON 时,所有 CreditCardDetails 类型的属性(如 customer.creditCardDetails)都会被完全忽略,不会出现在生成的 JSON 字符串中。
    • 在此例中,生成的 json 字符串将不含 creditCardDetails 部分,仅包含 customerIdname
    • 同理,在反序列化过程中,如果 JSON 数据中包含 CreditCardDetails 类型的嵌套结构,Jackson 解析时会自动忽略这部分内容,不会尝试创建或填充对应的 CreditCardDetails 对象

7.1.4 @JsonProperty

用于指定类的属性在序列化和反序列化成 JSON 时所对应的键名

  1. 假设有一个 Book 类,其中包含 titleyearPublished 字段。我们希望在序列化和反序列化时,将 yearPublished 字段以 publishedYear 作为 JSON 键名。可以使用 @JsonProperty 注解进行重命名

    public class Book {
        private String title;
        @JsonProperty("publishedYear") // 重命名 yearPublished 字段为 publishedYear
        private int yearPublished;
        // 构造器、getter/setter 方法等...
    }
    
  2. 使用示例

    lic void testJsonProperty() throws Exception {
        Book book = new Book();
        book.setTitle("小凡编程语录");
        book.setYearPublished(1951);
    
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(book);
        System.out.println(json);
    }
    //输出
    {
      "title": "小凡编程语录",
      "publishedYear": 1951
    }
    
  3. 详细解释

    在本例中,@JsonProperty("publishedYear") 表明 yearPublished 字段在 JSON 中应称为 publishedYear

    反序列化时,当遇到 JSON 中的 publishedYear 键时,Jackson 会知道应该将其值赋给 Book 类的 yearPublished 字段。

7.1.5 @JsonFormat

用于指定日期、时间、日期时间以及其他数值类型在序列化和反序列化为 JSON 时的格式

public class Student {
    // 其他属性...

    // 使用 @JsonFormat 注解的 LocalDate 属性
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date birthday;
    // 构造器、getter、setter 等...
}

**注:**Jackson默认不支持Java 8的日期时间类型java.time.LocalDate

7.2 序列化注解

7.2.1 @JsonInclude

用于控制对象在序列化过程中包含哪些属性。它可以防止空或特定值的字段被序列化到JSON输出中,从而帮助您精简JSON数据结构,减少不必要的传输量或避免向客户端暴露不必要的信息。

  1. 定义PersonInclude实体类

    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class PersonInclude {
    
        /**
         * 姓名字段,由于未指定@JsonInclude注解,因此继承类级别的NON_NULL策略
         */
        private String name;
    
        /**
         * 职业字段,显式指定为仅在非空时才包含在序列化结果中
         */
        @JsonInclude(JsonInclude.Include.NON_EMPTY)
        private String occupation;
    
        /**
         * 兴趣爱好列表,遵循类级别的NON_NULL策略
         */
        private List<String> hobbies;
    
        // 构造函数、getter/setter等省略...
    
    }
    
  2. 使用示例

    @Test
    public void testJsonInclude() throws JsonProcessingException {
        PersonInclude personFull = new PersonInclude();
        personFull.setName("小凡");
        personFull.setOccupation("程序员");
        personFull.setHobbies(Arrays.asList("编程","看书","打豆豆"));
    
        PersonInclude personPartial = new PersonInclude();
        personPartial.setName(null);
        personPartial.setOccupation("保洁员");
    
    
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(personFull);
        System.out.println(json);
    
        String json1 = mapper.writeValueAsString(personPartial);
        System.out.println(json1);
    
    
    }
    //输出
    {"name":"小凡","occupation":"程序员","hobbies":["编程","看书","打豆豆"]}
    {"occupation":"保洁员"}
    
  3. 详细解释

    • 测试用例1:序列化后,JSON只包含nameoccupationhobbies字段都不为空,所以都显示出来
    • 测试用例2:创建了一个部分字段为null或空的PersonInclude实例。为空的不显示

7.2.2 @JsonGetter

用于告诉Jackson,应该通过调用getter方法而不是通过直接字段访问来获取某个字段值

  1. 创建PersonGetter实体类

    public class PersonGetter {
        private String firstName;
        private String lastName;
    
        // 使用 @JsonGetter 定义一个方法,该方法将在序列化时作为 "fullName" 属性的值返回
        @JsonGetter("fullName")
        public String getFullName() {
            return this.firstName + " " + this.lastName;
        }
       // 构造函数和其他常规setter/getter方法
    }
    
  2. 使用示例

    @Test
    public void testJsonGetter() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例
    
        PersonGetter person = new PersonGetter("张", "小凡");// 创建一个 Person 对象
    
        String json = mapper.writeValueAsString(person); // 将 Person 对象序列化为 JSON 字符串
    
        System.out.println(json); // 输出序列化后的 JSON
    }
    //输出
    {"firstName":"张","lastName":"小凡","fullName":"张 小凡"}
    

7.2.3 @JsonAnyGetter

用于标记一个方法,获取除已知属性外的所有其他键值对。这些键值对通常存储在一个 Map 结构中,以便将它们作为一个附加的对象进行序列化。

  1. 创建CustomData实体类

    @Data
    public class CustomData {
        private final Map<String, Object> additionalProperties = new HashMap<>();
    
        // 使用 @JsonAnyGetter 定义一个方法,该方法将在序列化时返回所有额外属性
        @JsonAnyGetter
        public Map<String, Object> getAdditionalProperties() {
            return additionalProperties;
        }
    
        // 提供方法来添加或修改额外属性
        public void addProperty(String key, Object value) {
            additionalProperties.put(key, value);
        }
    }
    
  2. 使用示例

     @Test
    public void testJsonAnyGetter() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例
    
        CustomData data = new CustomData();
        data.addProperty("key1", "value1");
        data.addProperty("key2", 42);
        data.addProperty("key3", "value3");
    
        String json = mapper.writeValueAsString(data); // 将 CustomData 对象序列化为 JSON 字符串
    
        System.out.println(json); // 输出序列化后的 JSON
    }
    //输出
    {"key1":"value1","key2":42,"key3":"value3"}
    

@JsonAnyGetter 注解的主要作用如下:

  • 动态属性支持:通过在返回 Map 的方法上使用 @JsonAnyGetter,您可以将一个对象的动态属性集合序列化为 JSON 对象的多个键值对。这些属性可能是在运行时添加的,或者基于某些条件动态生成的。
  • 简化结构:避免为每个可能的动态属性单独声明字段和 getter/setter。只需维护一个 Map,即可处理任意数量和类型的额外属性。
  • 兼容性与灵活性:当需要与未知或未来可能变化的数据结构交互时,@JsonAnyGetter 可确保 JSON 表示能够容纳未预定义的属性,从而提高系统的兼容性和适应性。

7.2.4 @JsonPropertyOrder

用于指定类中属性在序列化为 JSON 时的排序规则

  1. 创建OrderPerson 实体类

    public class OrderPerson {
        @JsonProperty("firstName")
        private String givenName;
    
        @JsonProperty("lastName")
        private String familyName;
        // 构造函数和其他常规setter/getter方法
    }
    
  2. 使用示例

     @Test
    public void testJsonPropertyOrder() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例
    
        OrderPerson person = new OrderPerson("John", "Doe"); // 创建一个 OrderedPerson 对象
    
        String json = mapper.writeValueAsString(person); // 将 OrderedPerson 对象序列化为 JSON 字符串
    
        System.out.println(json); // 输出序列化后的 JSON
    }
    //输出
    {"firstName":"John","lastName":"Doe"}
    

@JsonPropertyOrder 注解按照指定顺序("lastName""firstName")对 JSON 对象的属性进行了排序。

7.2.5 @JsonRawValue

用于标记一个字段或方法返回值,指示Jackson在序列化时应将其原始值视为未经转义的 JSON 字符串,并直接嵌入到输出的 JSON 文档中。即使所标记的值包含 JSON 特殊字符(如双引号、反斜杠等),也不会对其进行转义

  1. 创建一个包含 @JsonRawValue 注解的类

    public class RawJsonValueExample {
        private String normalProperty = "这是一个正常字符串";
    
        // 使用 @JsonRawValue 标记 rawJsonProperty,使其内容被视为未经转义的 JSON 值
        @JsonRawValue
        private String rawJsonProperty = "{\"key\": \"value\", \"array\": [1, 2, 3]}";
    
        // 构造函数和其他常规setter/getter方法
    }
    
  2. 使用示例

    @Test
    public void testJsonRawValue() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例
    
        RawJsonValueExample example = new RawJsonValueExample(); // 创建一个 RawJsonValueExample 对象
    
        String json = mapper.writeValueAsString(example); // 将 RawJsonValueExample 对象序列化为 JSON 字符串
    
        System.out.println(json); // 输出序列化后的 JSON
    }
    //输出
    {"normalProperty":"这是一个正常字符串","rawJsonProperty":{"key": "value", "array": [1, 2, 3]}}
    

rawJsonProperty 的值并未被转义,而是作为一个完整的 JSON 对象直接嵌入到父 JSON 文档中。

当您需要在 JSON 对象中嵌入另一段 JSON 文本时,使用 @JsonRawValue 可以确保这段文本以未经转义的形式出现在输出的 JSON 中,保持其原有的 JSON 结构。

7.2.6 @JsonValue

用于标记一个方法或字段,Jackson在序列化该类实例时,直接使用该方法的返回值或字段的值作为整个对象的 JSON 表示,而非按照类的常规属性进行序列化

  1. 创建JsonValueExample

    public class JsonValueExample {
        private LocalDate date;
        private String formattedDate;
    
        @JsonValue
        public String asJsonString() {
            return "{\"date\":\"" + date.toString() + "\",\"formatted\":\"" + formattedDate + "\"}";
        }
        // 构造函数和其他常规setter/getter方法
    }
    
  2. 使用示例

    @Test
    public void testJsonValue() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
    
        JsonValueExample example = new JsonValueExample();
        example.setDate(LocalDate.of(2024,4,6));
        example.setFormattedDate("yyyy-MM-dd");
    
        String json = mapper.writeValueAsString(example);
    
        System.out.println(json);
    }
    //输出
    "{\"date\":\"2024-04-06\",\"formatted\":\"yyyy-MM-dd\"}"
    

asJsonString() 方法上应用注解,指示序列化时应将该方法的返回值作为整个对象的 JSON 表达,忽略类中其他的属性

7.2.7 @JsonSerialize

于指定类、字段或方法在序列化过程中使用的自定义序列化逻辑

  1. 创建一个包含 @JsonSerialize 注解的类及其自定义序列化器

    public class BooleanSerializer extends JsonSerializer<Boolean> {
        @Override
        public void serialize(Boolean aBoolean, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if(aBoolean){
                jsonGenerator.writeNumber(1);
            } else {
                jsonGenerator.writeNumber(0);
            }
        }
    }
    
    public class PersonSerializer {
        public long   personId = 0;
        public String name     = "John";
    
        @JsonSerialize(using = BooleanSerializer.class)
        public boolean enabled = false;
        // 构造函数和其他常规setter/getter方法
    }
    
  2. 使用示例

    @Test
    public void testJsonSerialize() throws JsonProcessingException {
        PersonSerializer person = new PersonSerializer();
        person.setName("小凡");
        person.setPersonId(1001);
        person.setEnabled(true);
    
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(person);
        System.out.println(json);
    }
    //输出
    {"personId":1001,"name":"小凡","enabled":1}
    

通过上面代码测试我们可以看到,enabled被序列化为1

7.3 反序列化注解

7.3.1 @JsonSetter

用于标记一个方法或字段,指示 Jackson 在反序列化过程中应如何设置该属性的值

  1. 创建PersonSetter

    public class PersonSetter {
        @JsonSetter("first_name")
        private String firstName;
        @JsonSetter("last_name")
        private String lastName;
      // 其他getter setter toString方法省略...
    }
    
  2. 使用示例

    @Test
    public void testJsonSetter() throws JsonProcessingException {
    
        String jsonNormal = "{\"first_name\":\"张\", \"last_name\":\"小凡\"}";
        ObjectMapper mapper = new ObjectMapper();
        PersonSetter person = mapper.readValue(jsonNormal, PersonSetter.class);
        System.out.println(person);
    }
    //输出
    PersonSetter(firstName=张, lastName=小凡)
    

@JsonSetter 注解提供了灵活的方式来定制Jackson在反序列化期间如何将JSON数据映射到Java对象的属性

7.3.2 @JsonAnySetter

用于处理反序列化过程中遇到的未知或额外的 JSON 键值对。当一个对象的JSON表示中包含无法直接映射到已声明属性的键时,这个注解可以帮助捕获并存储这些额外的数据

  1. 定义带有 @JsonAnySetter 注解的类
@ToString
public class CustomObject {
    private String knownProperty;
    private Map<String, Object> additionalProperties = new HashMap<>();

    // 已知属性的setter方法
    public void setKnownProperty(String knownProperty) {
        this.knownProperty = knownProperty;
    }

    // 获取已知属性的getter方法省略...

    /**
     * @JsonAnySetter注解方法,用于处理反序列化过程中遇到的所有未知属性。
     *
     * @param key   未知属性的键
     * @param value 未知属性的值
     */
    @JsonAnySetter
    public void addAdditionalProperty(String key, Object value) {
        additionalProperties.put(key, value);
    }

    // 提供访问额外属性的方法
    public Map<String, Object> getAdditionalProperties() {
        return additionalProperties;
    }
}
  1. 使用示例

    @Test
    public void testJsonAnySetter() throws JsonProcessingException {
        String json ="{\"knownProperty\":\"expectedValue\",\"extraField1\":\"someValue\",\"extraField2\":42,\"nestedObject\":{\"key\":\"value\"}}";
    
        ObjectMapper mapper = new ObjectMapper();
    
        CustomObject customObject = mapper.readValue(json, CustomObject.class);
        System.out.println(customObject);
    }
    //输出
    CustomObject(knownProperty=expectedValue, additionalProperties={extraField1=someValue, nestedObject={key=value}, extraField2=42})
    

@JsonAnySetter注解来应对JSON反序列化过程中可能出现的未知属性,确保所有数据都能被妥善处理和保留。这种机制特别适用于需要兼容动态或扩展性较强的JSON输入场景。

7.3.3 @JsonCreator

用于标记一个构造器、静态工厂方法或实例方法,使其成为反序列化过程中创建对象实例的入口点。这个注解帮助 Jackson 确定如何根据 JSON 数据构建相应的 Java 对象。

  1. 定义带有 @JsonCreator 注解的类

    public class AddressCreator {
        private  String street;
        private  int number;
        private  String city;
    
        // 构造器上使用@JsonCreator注解,指示Jackson使用此构造器反序列化JSON
        @JsonCreator
        public AddressCreator(@JsonProperty("street") String street,
                       @JsonProperty("number") int number,
                       @JsonProperty("city") String city) {
            this.street = street;
            this.number = number;
            this.city = city;
        }
        // getter setter toString方法省略...
    }
    
  2. 使用示例

@Test
public void testCreateor() throws JsonProcessingException {
    String json ="{\"street\":\"呈贡区\",\"number\":123,\"city\":\"昆明\"}";
    ObjectMapper mapper = new ObjectMapper();
    AddressCreator addressCreator = mapper.readValue(json, AddressCreator.class);
    System.out.println(addressCreator);
}
//输出
AddressCreator(street=呈贡区, number=123, city=昆明)

7.3.4 @JacksonInject

用于在反序列化过程中自动注入依赖项或附加信息到目标对象。通常用于处理那些不能直接从 JSON 数据中获取、但又希望在反序列化完成后立即可用的信息

  1. 定义带有 @JacksonInject 注解的类

    public class UserInject {
        private String name;
        private String email;
    
        // @JacksonInject注解在字段上,指示Jackson在反序列化过程中注入特定值
        @JacksonInject("defaultEmail")
        private String defaultEmail;
    
        // 构造器和getter setter toString方法省略...
    }
    
  2. 使用示例

    @Test
    public void testJacksonInject() throws JsonProcessingException {
        // 设置可注入的值
        InjectableValues injectables = new InjectableValues.Std().addValue("defaultEmail", "xiaofan@example.com");
    
        // 创建ObjectMapper并配置注入值
        ObjectMapper mapper = new ObjectMapper().setInjectableValues(injectables);
    
        String json = "{\"name\":\"小凡\"}";
    
        // 反序列化时,defaultEmail字段会被自动注入指定值
        UserInject userInject = mapper.readValue(json, UserInject.class);
    
        System.out.println("Name: " + userInject.getName());
        System.out.println("Email: " + userInject.getEmail());
        System.out.println("Default Email: " + userInject.getDefaultEmail());
    }
    //输出
    Name: 小凡
    Email: null
    Default Email: xiaofan@example.com
    

@JacksonInject 注解在Jackson反序列化过程中用于引入外部依赖或默认值,使得这些信息能够在反序列化完成后立即可用

7.3.5 @JsonDeserialize

用于在反序列化过程中指定自定义的反序列化器来处理某个字段或类的特殊逻辑

  1. 自定义反序列化器

    public class BooleanDeserializer extends JsonDeserializer<Boolean> {
        @Override
        public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
            String text = jsonParser.getText();
            if("0".equals(text)) {
                return false;
            }
            return true;
        }
    }
    
  2. @JsonDeserialize注解添加到要为其使用自定义反序列化器的字段

    public class PersonDeserialize {
        public long    id;
        public String  name;
    
        @JsonDeserialize(using = BooleanDeserializer.class)
        public boolean enabled = false;
        //构造器和getter setter toString方法省略...
    }
    
  3. 使用示例

    {"id":1001,"name":"小凡","enabled":1}
    
    @Test
    public void testJsonDeserialize() throws JsonProcessingException {
        String json ="{\"id\":1001,\"name\":\"小凡\",\"enabled\":1}";
        ObjectMapper mapper = new ObjectMapper();
        PersonDeserialize personDeserialize = mapper.readValue(json, PersonDeserialize.class);
        System.out.println(personDeserialize);
    }
    //输出
    PersonDeserialize(id=1001, name=小凡, enabled=true)
    

我们json字符串中enabled的值是1,最终反序列化成PersonDeserialize 对象后值变成了ture