【Jackson】实现 Java 中的 JSON 对象映射

哈喽,哈喽,大家好~ 我是你们的老朋友:保护小周ღ  


今天给大家带来的是 【Jackson】实现 Java 中的 JSON 对象映射,首先了解, Jackson 库是干啥的, 然后如何进行对象与 JSON 结构之间的序列化和反序列化的使用, 以及 Jackson 常用注解的使用, 和全局统一配置(默认属性) 一起来看看叭~


本期收录于博主的专栏JavaEE_保护小周ღ的博客-CSDN博客

适用于编程初学者,感兴趣的朋友们可以订阅,查看其它 “JavaEE基础知识”。

更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘ 


 在现代应用程序开发中,JSON(JavaScript Object Notation)是数据交换的标准格式(前后端交互)。Java提供了多种工具和库来实现 JSON 解析和对象映射,使开发者能够高效地处理数据。

JSON 对象映射是将 JSON 数据转换为 Java 对象的过程,反之亦然。这个过程也有一种说法叫: 序列化(对象转 JSON)和反序列化(JSON 转对象).

一、Jackson 库

Jackson 是一个高效的开源 Java 库,用于处理 JSON 数据。它由 FasterXML 提供,广泛应用于 Java 应用程序中进行 JSON 的序列化和反序列化。Spring MVC 的默认 json 解析器便是 Jackson (内置)它支持将 Java 对象转换为 JSON 格式(序列化),以及将 JSON 数据解析回 Java 对象(反序列化)。同时 Jackson 也可以将 Map 转换为 JSON 格式 以及反向操作将 JSON 字符串转换回 Map.


1.1 添加依赖

要想使用 Jackson库的功能模块, 就需要在项目中的 pom.xml 配置文件中添加依赖:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.8</version>
</dependency>

注意:

1.  Jsckson 依赖的版本与 JDK 的版本是否兼容.

2. 如果咱们得项目是 SpringMvc / SpringBoot 就是默认集成 Jackson 作为 JSON 处理的默认库, 无需额外添加依赖即可使用,  SpringBoot项目使用的前提是, 在项目中引入了 SpringWeb (SpringMvc 的优化依赖/ 另一个名字) 依赖. SprongBoot 框架会自动的适配依赖之间的版本是否兼容, 这是相较于 Spring 框架的优化之一.


1.2 序列化/反序列化

Jackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper类提供的。ObjectMapper 是 Jackson 提供的核心类,用于处理 JSON 数据的序列化和反序列化。

我们定义一个 User 类来进行操作演示: 

@Data
public class User {
   private String username;
   private String password;
   private String email;
}

@Data 是一个Lombok注解,它可以帮助我们自动生成一些常用的方法,如Getter、Setter、ToString、EqualsAndHashCode等, 避免了手动编写大量重复的getter和setter方法。


1.2.1  将对象序列化为 JSON 类型

witreValueAsString() 方法: 用于将 Java 对象序列化为 JSON 字符串。

参数(value): 是要被序列化的Java 类对象.

public class Start {
    public static void main(String[] args) {
        // Jackson 核心类 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setEmail("3391641***@qq.com");

        try {
            // 将一个对象序列化为 JSON
            String userJson = objectMapper.writeValueAsString(user);
            System.out.println(userJson);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

结果如图所示: 标准的Json 格式


1.2.2  将 JSON 反序列化为类对象

readValue() 方法, 用于将 JSON 字符串反序列化为 Java 对象。

参数:  

  • content: 要被反序列化的 JSON 字符串。
  • valueType: 目标 Java 类的类型(例如 User.class),指定将 JSON 字符串转换成哪种类型的对象。
public class Start {
    public static void main(String[] args) {
        // Jackson 核心类 ObjectM
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setEmail("3391641***@qq.com");

        try {
            // 将一个对象序列化为 JSON
            String userJson = objectMapper.writeValueAsString(user);
            // 打印 User 对象序列化后的 Json 字符串
            System.out.println("序列化userJson打印: " + userJson);

            // 将 Json 字符串反序列化为 User对象
            User newUser = objectMapper.readValue(userJson, User.class);
            // 反序列化得到的 newUser 对象
            System.out.println("反序列化newUser打印: "+ newUser.toString());

        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

结果如图所示: 


1.2.3 Map 结构的序列化和反序列化

已上述 User 类为例, 添加 Map 结构作为成员变量, 并针对 map 成员变量重写 get / set 方法.

public class User {

   private String username;
   private String password;
   private String email;

   // 添加 Map 结构作为成员变量
   private Map<String, Object> message = new HashMap<>();

   // Jackson 核心对象.
   private ObjectMapper objectMapper = new ObjectMapper();

   /**
    * 重写 get 方法, 直接获取将 Map 结构转换为 Json 字符串
    * 方便存储于数据库, 数据库对应字段可用字符串类型接收
    * @return json 字符串
    */
   public String getMessage() {
       try {
           return objectMapper.writeValueAsString(this.getMessage());
           
       } catch (JsonProcessingException e) {
           e.printStackTrace();
       }
       return "{}";
   }

   /**
    * 重写 setMessage 方法, json 字符串 转换为 Map 类型, 并赋值给 message
    * 使用场景: 该字段数据库存储与恢复
    * @param messageJson
    */
   public void setMessage(String messageJson) {
       try {
           this.message = objectMapper.readValue(messageJson, new TypeReference<Map<String, Object>>() {});
           
       } catch (JsonProcessingException e) {
           e.printStackTrace();
       }
   }
}

 使用效果:

 public class Start {
    public static void main(String[] args) {
        // Jackson 核心类 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setEmail("3391641***@qq.com");

        // 1. 用于给 User 对象的 message 赋值
        HashMap<String, Object> message = new HashMap<>();
        message.put("name", "张三");
        message.put("age", "18");
        message.put("sex", "男");

        try {
            // 2. 调用 User 对象 setMessage 方法将 json 转化为 map 并赋值
            String messageJson = objectMapper.writeValueAsString(message);
            user.setMessage(messageJson);

            // 3. 调用 User 对象 getMessage 方法读取 map 转换的 json
            String newMessageJson = user.getMessage();
            System.out.println(newMessageJson);

        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

 

针对 User 类的 message (Map) 成员变量进行序列化和反序列化的设计目的是为了方便, 存储, 将该字段在数据库中用字符串类型接收, 减少了复杂度.


 1.2.4 常见异常及其原因

在使用 ObjectMapper 类方法时可能会遇到一些常见的异常,以下是一些可能的原因及解决方案:

 1. JsonProcessingException

  • 原因: 这是 ObjectMapper 在序列化过程中最常见的异常,表示 JSON 处理错误。
  • 解决方案: 确保你的 Java 对象(如 user)被正确设置并且没有循环引用。检查是否有任何未处理的字段或对象。
try {
    String jsonString = objectMapper.writeValueAsString(user);
} catch (JsonProcessingException e) {
    e.printStackTrace(); // 处理异常
}

2.  NullPointerException

  • 原因: 如果你尝试序列化一个为 null 的对象,可能会抛出 NullPointerException
  • 解决方案: 确保在调用 writeValueAsString 之前,所传递的对象不是 null
if (user != null) {
    String jsonString = objectMapper.writeValueAsString(user);
} else {
    // 处理 null 情况
}

3.  IllegalArgumentException

  • 原因: 如果你传递了一个不支持序列化的类型(例如某些自定义类型或接口),可能会引发此异常。
  • 解决方案: 确保你的对象是可序列化的,并且所有字段都具有公共的 getter 方法。

4. 其他注意事项

  • 确保字段有 getter 方法: Jackson 需要访问对象的字段,通常通过 getter 方法来进行。如果字段是私有的但没有相应的 getter,Jackson 将无法序列化该字段。
public class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
  • 循环引用: 如果你的对象中存在循环引用,例如 A 包含 B,B 又包含 A,这会导致 StackOverflowError。可以使用 @JsonManagedReference@JsonBackReference 注解来处理(后面会讲到)。

  • 版本控制: 如果使用不同版本的 Jackson 库,可能会因为库之间的 API 不兼容而出现问题。确保项目中使用的 Jackson 版本一致。

 注意: 确保在运行时捕获异常,以便可以看到具体的错误信息。有助于调试和修复问题。


1.3 Jackson 的常用注解

@JsonProperty

指定 JSON 属性与 Java 对象字段之间的映射 / 可以用于设置字段名、描述是否必需

@JsonIgnore

在序列化或反序列化过程中忽略某个字段。

@JsonInclude

控制序列化时包含的字段。例如,可以只包含非空值。

@JsonFormat

用于格式化日期和时间字段的输出,指定输出格式。

@JsonCreator

标注构造函数或静态工厂方法,以便在反序列化时使用。

@JsonValue

将方法的返回值作为 JSON 序列化的代表值。

@JsonAnyGette/

@JsonAnySetter

处理动态属性,允许在对象中存储不固定的字段。

@JsonRootName

用于指定序列化和反序列化时的根元素名称。

1.3.1 @JsonProperty

正常情况下, 都是使用字段名一致的, 减少错误的发生.

用途:指定 JSON 属性与 Java 对象字段之间的映射。可以用于设置字段名、描述是否必需等。

public class User {
    @JsonProperty("name") // JSON 中的 "name" 会映射到 userName 字段
    private String userName;

    @JsonProperty(required = true) // 指定该字段为必需
    private int age;

    // getters and setters
}

1.3.2 @JsonIgnore

用途:在序列化或反序列化过程中忽略某个字段。

@Data
public class User {

   private String username;
   
    /**
     * 此字段不会被序列化到 json, 在前后端交互中, 把密码传来传去这不合适
     */
   @JsonIgnore
   private String password;
   private String email;
}

单元测试: 

    @Test
    public void jsonIgnoreTest() {
        // Jackson 核心类 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setEmail("3391641***@qq.com");

        try {
            String userJson = objectMapper.writeValueAsString(user);
            System.out.println(userJson);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

 被 @JsonIgnore 注解修饰的 password 字段将不会被序列化到 JSON

1.3.3 @JsonInclude

用途:控制序列化时包含的字段。例如,可以只包含非空值。

public class User {
    private String userName;

    @JsonInclude(JsonInclude.Include.NON_NULL) // 仅当不为 null 时才序列化
    private String address;

    // getters and setters
}

1.3.4 @JsonFormat

用途:用于格式化日期和时间字段的输出,指定输出格式。

public class User {
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") // 指定日期格式
    private LocalDate birthDate;

    // getters and setters
}

 1.3.5 @JsonCreator

用途:标注构造函数或静态工厂方法,以便在反序列化时使用

public class User {
    private String userName;

    @JsonCreator // 使用此构造函数进行反序列化
    public User(@JsonProperty("name") String userName) {
        this.userName = userName;
    }

    // getters and setters
}

1.4 Jackson 的统一配置

在使用 Jackson 进行 JSON 序列化和反序列化时,统一配置可以帮助设置全局的行为,如日期格式、字段命名策略、忽略空值等。以下是一些常见的统一配置方法:

1.4.1 使用 ObjectMapper 配置

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;

public class JacksonConfig {
    public static ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();

        // 设置日期格式
        objectMapper.setDateFormat(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        // 忽略未知属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 设置字段命名策略
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

        // 其他配置
        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); // 忽略 null 值

        return objectMapper;
    }
}

1.4.2 Spring Boot 中的统一配置

如果我们使用 Spring Boot项目,可以在 application.propertiesapplication.yml 配置文件中进行一些基本配置,或者通过 Java 配置类进一步自定义。

在 application.properties 中
# 日期格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# 忽略未知属性
spring.jackson.deserialization.fail-on-unknown-properties=false
# 忽略空值
spring.jackson.default-property-inclusion=non_null

application.yml 配置文件中

spring:
  jackson:
    # 设置日期格式
    date-format: yyyy-MM-dd HH:mm:ss
    
    # 忽略未知属性
    deserialization:
      fail-on-unknown-properties: false
    
    # 忽略空值
    default-property-inclusion: non_null
    
    # 自定义命名策略
    property-naming-strategy: SNAKE_CASE
    
    # 禁用写入空值
    serialization:
      write-null-map-values: false

 

自定义Java 配置类

可以通过创建一个配置类来进一步自定义 Jackson 的行为:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();

        // 全局配置
        objectMapper.setDateFormat(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

        return objectMapper;
    }
}

1.4.3 使用注解进行局部配置

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;

public class User {
    
    @JsonProperty("user_name") // 自定义字段名
    private String username;
    
    @JsonFormat(pattern = "yyyy-MM-dd") // 自定义日期格式
    private LocalDate birthDate;

    // Getters and Setters
}

好了,到这里,【Jackson】实现 Java 中的 JSON 对象映射 博主已经分享完了,阐述较为基础,  希望对大家有所帮助,如有不妥之处欢迎批评指正。 

感谢每一位观看本篇文章的朋友,更多精彩敬请期待:保护小周ღ *★,°*:.☆( ̄▽ ̄)/$:*.°★* 

遇见你,所有的星星都落在我的头上……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

保护小周ღ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值