写在前面
虽然现在市面上有很多优秀的 json 解析库,但 Spring 默认采用 Jackson 解析 Json。
本文将通过一系列通俗易懂的代码示例,带你逐步掌握 Jackson 的基础用法、进阶技巧以及在实际项目中的应用场景。
一、Jackjson 简介
Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。
什么是序列化和反序列化呢?
GitHub 地址:https://github.com/FasterXML/jackson
从 GitHub 看到,目前有 8.8k stars,最近更新时间是 2 个月前。可见其更新速度还是比较活跃的
也是 json 最流行的解析器之一
二、Jackjson 优点
Jackson 所依赖的 jar 包较少,简单易用
其他 Java 的 json 的框架 Gson 等相比, Jackson 解析大的 json 文件速度比较快
Jackson 运行时占用内存比较低,性能比较好
Jackson 有灵活的 API,可以很容易进行扩展和定制
三、Jackjson 模块及依赖
① Jackson 库包括三个主要的模块
② 所需依赖
jackson-databind 依赖 jackson-core 和 jackson-annotations,所以可以只显示地添加 jackson-databind 依赖,jackson-core 和 jackson-annotations 也随之添加到 Java 项目工程中
maven
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
复制代码
Gradle
dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4'
}
复制代码
四、快速上手
假设我们有一个简单的 User
类:
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}
复制代码
4.1 Java 对象转 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}
复制代码
4.2 JSON 字符串转 Java 对象
@Test
public void testDeserializationExample() throws JsonProcessingException {
String json ="{\"name\":\"小凡\",\"age\":18}";
ObjectMapper objectMapper = new ObjectMapper();
User user = objectMapper.readValue(json, User.class);
System.out.println(user);
}
//输出
User(name=小凡, age=18)
复制代码
上面例子中我们我们使用了ObjectMapper
它是 Jackson 库的核心类,负责实现 Java 对象与 JSON 文本之间的相互转换
五、Jackjson 序列化 API
5.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}
复制代码
5.2 复杂对象序列化
① 构造作者出版信息对象
//书籍对象
@Data
public class Book {
private String bookName;
private String publishDate;
private String publishHouse;
private Double price;
}
//地址对象
@Data
public class Address {
private String city;
private String street;
}
//作者出版信息对象
@Data
public class PublishInfo {
private String name;
private String sex;
private Integer age;
private Address addr;
private List<Book> books;
}
复制代码
② 复杂对象转 JSON 字符串
@Test
public void testComplexObjectToJson() throws JsonProcessingException {
//构造所有出版的书籍list
ArrayList<Book> books = new ArrayList<Book>();
Book book1 = new Book();
book1.setBookName("Java从入门到放弃");
book1.setPublishDate("2004-01-01");
book1.setPublishHouse("小凡出版社");
book1.setPrice(66.66);
Book book2 = new Book();
book2.setBookName("Spring从入门到入土");
book2.setPublishDate("2024-01-01");
book2.setPublishHouse("小凡出版社");
book2.setPrice(88.88);
books.add(book1);
books.add(book2);
//构造作者地址信息
Address addr = new Address();
addr.setCity("昆明");
addr.setStreet("xxx区xxx路xxx号");
//构造作者出版的所有书籍信息
PublishInfo publishInfo = new PublishInfo();
publishInfo.setName("小凡");
publishInfo.setSex("男");
publishInfo.setAge(18);
publishInfo.setAddr(addr);
publishInfo.setBooks(books);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(publishInfo);
System.out.println(json);
}
//返回
{"name":"小凡","sex":"男","age":18,"addr":{"city":"昆明","street":"xxx区xxx路xxx号"},"books":[{"bookName":"Java从入门到放弃","publishDate":"2004-01-01","publishHouse":"小凡出版社","price":66.66},{"bookName":"Spring从入门到入土","publishDate":"2024-01-01","publishHouse":"小凡出版社","price":88.88}]}
复制代码
5.3 List 集合序列化
@Test
public void testListToJson() throws JsonProcessingException {
User user1 = new User();
user1.setName("小凡001");
user1.setAge(18);
User user2 = new User();
user2.setName("小凡002");
user2.setAge(30);
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
ObjectMapper mapper = new ObjectMapper();
String userstr = mapper.writeValueAsString(users);
System.out.println(userstr);
}
//输出
[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]
复制代码
5.4 Map 集合序列化
①
@Test
public void testMapToJson() throws JsonProcessingException {
User user = new User();
user.setName("小凡");
user.setAge(18);
List<String> asList = Arrays.asList("抽烟", "喝酒", "烫头发");
HashMap<String, Object> map = new HashMap<>();
map.put("user", user);
map.put("hobby",asList);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(map);
System.out.println(json);
}
//输出
{"user":{"name":"小凡","age":18},"hobby":["抽烟","喝酒","烫头发"]}
复制代码
②
@Test
public void testMapToJsonSup() throws JsonProcessingException {
User user1 = new User();
user1.setName("小凡001");
user1.setAge(18);
User user2 = new User();
user2.setName("小凡002");
user2.setAge(30);
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
List<String> asList = Arrays.asList("抽烟", "喝酒", "烫头发");
HashMap<String, Object> map = new HashMap<>();
map.put("users", users);
map.put("hobby",asList);
map.put("name","张三");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(map);
System.out.println(json);
}
//输出
{"name":"张三","users":[{"name":"小凡001","age":18},{"name":"小凡002","age":30}],"hobby":["抽烟","喝酒","烫头发"]}
复制代码
5.5 日期处理
默认情况下,jackjson 会将日期类型属性序列化成 long 型值(自 1970 年 1 月 1 日以来的毫秒数)。显然这样格式的数据不符合人类直观查看
假设我们有个Person
对象
@Data
public class Person {
private String name;
private Date birthday;
}
复制代码
① 我们先来看看默认转换的结果
@Test
public void testDateToJsonDefault() throws JsonProcessingException {
Person person = new Person();
person.setName("小凡");
person.setBirthday(new Date());
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
System.out.println(json);
}
//输出
{"name":"小凡","birthday":1712220896407}
复制代码
② 通过SimpleDateFormat
将日期格式化成人类可看格式显示
@Test
public void testDateToJson() throws JsonProcessingException {
Person person = new Person();
person.setName("小凡");
person.setBirthday(new Date());
ObjectMapper mapper = new ObjectMapper();
//进行一下日期转换
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
mapper.setDateFormat(dateFormat);
String json = mapper.writeValueAsString(person);
System.out.println(json);
}
//输出 这个格式就人性化多了
{"name":"小凡","birthday":"2024-04-04"}
复制代码
六、Jackjson 反序列化 API
Jackson 通过将 JSON 字段的名称与 Java 对象中的 getter 和 setter 方法进行匹配,将 JSON 对象的字段映射到 Java 对象中的属性。
Jackson 删除了 getter 和 setter 方法名称的“ get”和“ set”部分,并将其余名称的第一个字符转换为小写。
6.1 普通 JSON 字符串反序列化
6.1.1 JSON 字符串->Java 对象
注: 这里我们还是使用前面小节中创建的User
实体类
@Test
public void testStrToObject() throws JsonProcessingException {
String json ="{\"name\":\"小凡\",\"age\":18}";
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
System.out.println(user);
}
//输出
User(name=小凡, age=18)
复制代码
6.1.2 字符输入流-->Java 对象
@Test
public void testReaderToObject() throws JsonProcessingException {
String json ="{\"name\":\"小医仙\",\"age\":18}";
Reader reader = new StringReader(json);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
System.out.println(user);
}
//输出
User(name=小医仙, age=18)
复制代码
6.1.3 字节输入流->Java 对象
① 创建 user001.json 文件,文件内容如下
②将字节输入流转换为User
对象
@Test
public void testInputStreamToObject() throws IOException {
FileInputStream inputStream = new FileInputStream("F:\\vueworkspace\\jackjson-demo\\jackjson-demo\\src\\json\\user001.json");
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(inputStream, User.class);
System.out.println(user);
}
}
//输出
User(name=萧炎, age=18)
复制代码
6.1.4 JSON 文件反->Java 对象
①我们准备一个 user.json 文件,内容如下
② 读取user.json
文件中内容,并转换成User
对象
@Test
public void testJsonfileToObject() throws IOException {
File file = new File("F:\\vueworkspace\\jackjson-demo\\jackjson-demo\\src\\json\\user.json");
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(file, User.class);
System.out.println(user);
}
//输出
User(name=萧炎, age=18)
复制代码
6.1.5 URL 文件->Java 对象
① 我们在网络上放一个user.json
资源文件,内容如下
② 通过 URL(java.net.URL) 将 JSON 转换成User
对象
@Test
public void testUrlToObject() throws IOException {
String url ="https://files.cnblogs.com/files/blogs/685650/user.json";
URL url1 = new URL(url);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(url1, User.class);
System.out.println(user);
}
//输出
User(name=紫妍, age=18)
复制代码
6.1.6 字节数组-> java 对象
@Test
public void testByteToObject() throws IOException {
String json ="{\"name\":\"韩雪\",\"age\":18}";
byte[] bytes = json.getBytes();
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(bytes, User.class);
System.out.println(user);
}
//输出
User(name=韩雪, age=18)
复制代码
6.2 JSON 数组字符串 反序列化
6.2.1 JSON 字符串->Map 集合
@Test
public void testJsonStrToMap() throws JsonProcessingException {
//{"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>>() {});
System.out.println(map);
}
//输出
{name=小凡, sex=男, age=18}
复制代码
6.2.2 JSON 数组字符串->List 集合
@Test
public void testJsonArrToList() throws JsonProcessingException {
//[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]
String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";
ObjectMapper mapper = new ObjectMapper();
List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {
});
System.out.println(users);
}
//输出
[User(name=小凡001, age=18), User(name=小凡002, age=30)]
复制代码
6.2.3 JSON 数组字符串->Java 对象数据
@Test
public void testJsonArrToObjArr() throws JsonProcessingException {
//[{"name":"小凡001","age":18},{"name":"小凡002","age":30}]
String json ="[{\"name\":\"小凡001\",\"age\":18},{\"name\":\"小凡002\",\"age\":30}]";
ObjectMapper mapper = new ObjectMapper();
User[] users = mapper.readValue(json, User[].class);
for (User user : users) {
System.out.println(user);
}
}
//输出
User(name=小凡001, age=18)
User(name=小凡002, age=30)
复制代码
七、自定义序列化反序列化
通过自定义序列化和反序列化可以使其更加灵活多变
7.1 自定义序列化
有时候,我们不需要 jackjson 默认的序列化方式。例如,JSON 中使用与 Java 对象中不同的属性名称,
或者不需要 Java 对象中的某个字段,这时我们就需要自定义序列化器
我们先来看看 User 实体对象的定义如下
@Data
public class User {
private String name;
private Integer age;
}
复制代码
按照下面默认序列化代码,我们最后得到的 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}
复制代码
而现在的需求升级了,再不创建或修改User
s 实体对象的情况下,我们想要得到{"username":"小凡","userage":18}
这样的字符串,
应该怎么办呢?
这时,我们就需要自定义序列化器,就可以轻松实现,具体代码如下
@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 objectMapper = new ObjectMapper();
objectMapper.registerModule(version1Module);
User user = new User("小凡", 18);
String json = objectMapper.writeValueAsString(user); // 使用版本1的序列化器
System.out.println( json);
}
//输出
{"username":"小凡","userage":18}
复制代码
7.2 自定义反序列化器
同理,反序列化也可以自定义,具体代码如下
① 自定义一个反序列化器
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("name").asText();
int age = (Integer) ((IntNode) node.get("age")).numberValue();
return new User(name, age);
}
}
复制代码
②将 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 = "{\"name\":\"小凡\",\"age\":30}";
User user = objectMapper.readValue(json, User.class);
System.out.println(user);
}
//输出
User(name=小凡, age=30)
复制代码
八、树模型
Jackson 具有内置的树模型,可用于表示 JSON 对象。Jackson 树模型由 JsonNode 类表示。
在处理 JSON 时,我们有时并不直接关心或无法直接映射到特定的 Java 对象,而是需要对 JSON 内容进行动态、灵活的操作。
这时,树模型就派上了用场。我们可以遍历、查询、更新或合并 JSON 数据,而无需预先定义对应的 Java 类
8.1 JsonNode 类型概览
JsonNode 家族包括以下主要子类型:
ObjectNode:对应 JSON 对象,包含一组键值对,可以通过.put(key, value)
添加或更新属性。
ArrayNode:对应 JSON 数组,包含一系列元素,可通过.add(value)
插入新元素。
TextNode、IntNode、LongNode、DoubleNode 等:分别对应 JSON 中的字符串、整数、长整数、浮点数等基本类型值。
BooleanNode、NullNode:分别对应 JSON 中的布尔值和 null 值。
8.2 创建与操作 JsonNode 实例
下面通过一些具体的代码示例,展示如何使用 JackJson 的树模型 API 来创建、操作 JsonNode 对象。
示例 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.toString());
}
//输出
{"name":"小凡","age":18,"hobbies":["写代码","看书","打豆豆"]}
复制代码
上述代码首先创建了一个ObjectMapper
实例,它是 JackJson 的核心工具类,负责序列化和反序列化工作。接着,我们创建了一个ObjectNode
代表 JSON 对象,设置了"name"和"age"两个属性。然后创建了一个ArrayNode
存储爱好列表,并将其添加到personNode
中。最后,打印输出构建的 JSON 字符串。
示例 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());
}
//输出
小凡
18
写代码
看书
打豆豆
复制代码
在此示例中,我们首先定义了一个 JSON 字符串。然后使用ObjectMapper
的readTree()
方法将其反序列化为 JsonNode。接下来,通过调用.get(key)
方法访问对象属性或数组元素,并使用.asText()
、.asInt()
等方法获取其值。
8.3 利用 JackJson 树模型进行深度查询、修改、合并等操作
上面给出的两个案例属于入门级操作,我们学会后可以接着进行一些高级操作
@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));
}
//输出
Age: 18
First hobby: 写代码
{
"name" : "小凡",
"age" : 20,
"hobbies" : [ "写代码", "看书", "打豆豆", "旅行" ],
"address" : "北京市",
"job" : "程序员"
}
复制代码
九、Jackson 注解使用
9.1 序列化反序列化通用注解
9.1.1 @JsonIgnore
@JsonIgnore
用于在序列化或反序列化过程中忽略某个属性
① 定义实体类Person
public class Person {
private String name;
@JsonIgnore // 添加此注解以忽略 password 字段
private String password;
// 构造器、getter/setter 方法等...
}
复制代码
②使用示例
@Test
public void testJsonIgnore() throws Exception{
Person person = new Person("小凡", "123456");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
}
//输出
{
"name": "小凡"
}
复制代码
③ 详细解释
@JsonIgnore
注解直接放在属性声明前,如 private String password;
行上方。
当 Person
类的对象被序列化为 JSON 时,password
属性将不会出现在生成的 JSON 字符串中
同样地,在反序列化过程中,如果 JSON 数据中包含 password
字段,Jackson
在解析时会自动忽略它,不会尝试将其值设置到对应的 Person
对象属性上。
9.1.2 @JsonIgnoreProperties
与@JsonIgnore
注解相比,@JsonIgnoreProperties
用于批量指定在序列化或反序列化过程中应忽略的属性列表,特别适用于应对不明确或动态变化的输入 JSON 中可能存在但不应处理的额外字段
① 定义实体类Person
希望同时忽略 password
和 socialSecurityNumber
两个敏感属性
@JsonIgnoreProperties({"password", "socialSecurityNumber"})
public class Person {
private String name;
private String password;
private String socialSecurityNumber;
// 构造器、getter/setter 方法等...
}
复制代码
② 使用示例
@Test
public void testJsonIgnoreProperties() throws Exception{
Person person = new Person("小凡", "123456","233535");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
}
//输出
{
"name": "小凡"
}
复制代码
③ 详细解释
@JsonIgnoreProperties
注解放置在类定义的开始处,作为类级别的注解。
注解内通过一个字符串数组参数列举出要忽略的属性名。在这个例子中,{"password", "socialSecurityNumber"}
指定了 password
和 socialSecurityNumber
两个属性在序列化和反序列化时应被忽略。
序列化 Person
对象时,生成的 JSON 字符串将不包含 password
和 socialSecurityNumber
属性。与前面 @JsonIgnore
示例类似,JSON 输出仅包含未被忽略的 name
属性:
④ @JsonIgnoreProperties 还有一个额外功能
@JsonIgnoreProperties
有个可选的属性 ignoreUnknown
,用于控制是否忽略 JSON 中存在但 Java 类中没有对应的未知属性。若设置为 true
,则在反序列化时遇到未知属性时会自动忽略,避免抛出异常
@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
// ...
}
复制代码
9.1.3 @JsonIgnoreType
@JsonIgnoreType
注解在 Jackson
库中用于指示整个类在序列化或反序列化过程中应当被忽略。这适用于那些由于某种原因(如敏感信息、内部细节等)不需要或不应该被 JSON 化处理的类
①定义CreditCardDetails
实体类
// 标记该类在序列化和反序列化时应被忽略
@JsonIgnoreType
public class CreditCardDetails {
private String cardNumber;
private String cvv;
private String expirationDate;
// 构造器、getter/setter 方法等...
}
复制代码
②定义Customer
类
Customer
类,它有一个 creditCardDetails
属性引用上述 CreditCardDetails
类型的对象:
public class Customer {
private String customerId;
private String name;
private CreditCardDetails creditCardDetails;
// 构造器、getter/setter 方法等...
}
复制代码
③ 使用示例
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"
}
复制代码
④ 详细解释
在此例中,生成的 json
字符串将不含 creditCardDetails
部分,仅包含 customerId
和 name
:
{
"customerId": "CUST001",
"name": "John Doe"
}
复制代码
同理,在反序列化过程中,如果 JSON 数据中包含 CreditCardDetails
类型的嵌套结构,Jackson
解析时会自动忽略这部分内容,不会尝试创建或填充对应的 CreditCardDetails
对象
9.1.4 @JsonAutoDetect
@JsonAutoDetect
用于自定义类的属性(包括字段和 getter/setter 方法)在序列化和反序列化过程中的可见性规则。默认情况下,Jackson
只使用 public
的字段和 public
的 getter/setter 方法。通过使用 @JsonAutoDetect
,你可以根据需要调整这些规则,以适应不同类的设计和数据模型。
① 定义实体类Employee
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.NONE, // 不使用字段
getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC, // 允许 protected 和 public 的 getter
setterVisibility = JsonAutoDetect.Visibility.ANY, // 允许任意访问权限的 setter
isGetterVisibility = JsonAutoDetect.Visibility.DEFAULT, // 默认行为(public)
creatorVisibility = JsonAutoDetect.Visibility.DEFAULT) // 默认行为(public)
public class Employee {
protected String id;
String department; // package-private 字段
// 非标准命名的 getter
public String getIdentification() {
return id;
}
// 非标准命名的 setter
public void setIdentification(String id) {
this.id = id;
}
//...标准getter setter
}
复制代码
②使用示例
@Test
public void testJsonAutoDetect() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Employee employee = new Employee();
employee.setId("E001");
employee.setDepartment("Sales");
String json = mapper.writeValueAsString(employee);
System.out.println(json);
}
//输出
{
"identification": "E001",
"department": "Sales"
}
复制代码
③ 详细解释
在上述 Employee
类的例子中:fieldVisibility = JsonAutoDetect.Visibility.NONE
指定不使用任何字段,意味着即使有 public
字段,也不会被 Jackson
处理。getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC
允许 Jackson
使用 protected
和 public
的 getter 方法。setterVisibility = JsonAutoDetect.Visibility.ANY
指定可以使用任意访问权限的 setter 方法。isGetterVisibility
和 creatorVisibility
使用默认行为,即只处理 public
的 is-getter 方法和构造函数。
因为我们将 id
字段的 getter 和 setter 设为了非标准命名(getIdentification()
和 setIdentification()
),且指定了 getterVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC
,所以 Jackson
能正确识别并处理这两个方法,尽管它们不是标准的 get
前缀命名。
9.1.5 @JsonProperty
用于指定类的属性在序列化和反序列化成 JSON 时所对应的键名
①Book
实体类
假设有一个 Book
类,其中包含 title
和 yearPublished
字段。我们希望在序列化和反序列化时,将 yearPublished
字段以 publishedYear
作为 JSON 键名。可以使用 @JsonProperty
注解进行重命名:
public class Book {
private String title;
@JsonProperty("publishedYear") // 重命名 yearPublished 字段为 publishedYear
private int yearPublished;
// 构造器、getter/setter 方法等...
}
复制代码
②使用示例:
现在创建一个 Book 对象并序列化为 JSON:
public 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
}
复制代码
③详细解释
在本例中,@JsonProperty("publishedYear")
表明 yearPublished
字段在 JSON 中应称为 publishedYear
反序列化时,当遇到 JSON 中的 publishedYear
键时,Jackson
会知道应该将其值赋给 Book
类的 yearPublished
字段。
9.1.6 @JsonFormat
用于指定日期、时间、日期时间以及其他数值类型在序列化和反序列化为 JSON 时的格式
① 创建 Student
类
public class Student {
// 其他属性...
// 使用 @JsonFormat 注解的 LocalDate 属性
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate birthday;
// 构造器、getter、setter 等...
}
复制代码
②使用示例
注:Jackson 默认不支持 Java 8 的日期时间类型java.time.LocalDate
,如果使用 jdk8 需要引入如下依赖.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>最新版本号</version> <!-- 替换为当前最新的稳定版本 -->
</dependency>
复制代码
除了引入以来外,还需要确保在使用 Jackson 进行序列化和反序列化时,注册JavaTimeModule
mapper.registerModule(new JavaTimeModule());
@Test
public void testJsonFormat() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper(); // 创建 Jackson 的 ObjectMapper 实例
mapper.registerModule(new JavaTimeModule());
// 序列化示例
Student student = new Student();
student.setName("小凡");
student.setAge(18);
student.setBirthday(LocalDate.of(1990,1,1)); // 设置用户的出生日期
String json = mapper.writeValueAsString(student); // 将用户对象转换为 JSON 字符串
System.out.println(json); // 输出序列化后的 JSON 数据
// 反序列化示例
String inputJson = "{\"birthday\":\"1990-01-01\"}";
Student deserializedUser = mapper.readValue(inputJson, Student.class); // 从 JSON 字符串反序列化为 User 对象
System.out.println(deserializedUser.getBirthday()); // 输出反序列化后获得的出生日期
}
//输出
{"name":"小凡","age":18,"birthday":"1990-01-01"}
1990-01-01
复制代码
③详细解释
@JsonFormat
注解在 Jackson 中被用来精确控制日期/时间类型的属性在 JSON 序列化和反序列化过程中的格式。通过给定合适的 shape
和 pattern
参数,可以确保日期数据在 Java 类型与 JSON 文本之间准确无误地转换。
9.2 序列化注解
9.2.1 @JsonInclude
用于控制对象在序列化过程中包含哪些属性。它可以防止空或特定值的字段被序列化到 JSON 输出中,从而帮助您精简 JSON 数据结构,减少不必要的传输量或避免向客户端暴露不必要的信息
①定义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等省略...
}
复制代码
②使用示例
@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":"保洁员"}
复制代码
③ 详细解释
9.2.2 @JsonGetter
用于告诉 Jackson,应该通过调用 getter 方法而不是通过直接字段访问来获取某个字段值
①创建PersonGetter
实体类
public class PersonGetter {
private String firstName;
private String lastName;
// 使用 @JsonGetter 定义一个方法,该方法将在序列化时作为 "fullName" 属性的值返回
@JsonGetter("fullName")
public String getFullName() {
return this.firstName + " " + this.lastName;
}
// 构造函数和其他常规setter/getter方法
}
复制代码
②使用示例
@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":"张 小凡"}
复制代码
@JsonGetter
注解成功地将 getFullName()
方法的结果映射到 JSON 对象中的 "fullName"
属性。
9.2.3 @JsonAnyGetter
用于标记一个方法,获取除已知属性外的所有其他键值对。这些键值对通常存储在一个 Map 结构中,
以便将它们作为一个附加的对象进行序列化。
① 创建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);
}
}
复制代码
②使用示例
@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 表示能够容纳未预定义的属性,从而提高系统的兼容性和适应性。
9.2.4 @JsonPropertyOrder
用于指定类中属性在序列化为 JSON 时的排序规则
①创建OrderPerson
实体类
public class OrderPerson {
@JsonProperty("firstName")
private String givenName;
@JsonProperty("lastName")
private String familyName;
// 构造函数和其他常规setter/getter方法
}
复制代码
②使用示例
@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 对象的属性进行了排序。
9.2.5 @JsonRawValue
用于标记一个字段或方法返回值,指示 Jackson 在序列化时应将其原始值视为未经转义的 JSON 字符串,并直接嵌入到输出的 JSON 文档中。即使所标记的值包含 JSON 特殊字符(如双引号、反斜杠等),也不会对其进行转义
① 创建一个包含 @JsonRawValue
注解的类
public class RawJsonValueExample {
private String normalProperty = "这是一个正常字符串";
// 使用 @JsonRawValue 标记 rawJsonProperty,使其内容被视为未经转义的 JSON 值
@JsonRawValue
private String rawJsonProperty = "{\"key\": \"value\", \"array\": [1, 2, 3]}";
// 构造函数和其他常规setter/getter方法
}
复制代码
②使用示例
@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 结构。
9.2.6 @JsonValue
用于标记一个方法或字段,Jackson 在序列化该类实例时,直接使用该方法的返回值或字段的值作为整个对象的 JSON 表示,而非按照类的常规属性进行序列化
① 创建JsonValueExample
类
public class JsonValueExample {
private LocalDate date;
private String formattedDate;
@JsonValue
public String asJsonString() {
return "{\"date\":\"" + date.toString() + "\",\"formatted\":\"" + formattedDate + "\"}";
}
// 构造函数和其他常规setter/getter方法
}
复制代码
② 使用示例
@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 表达,忽略类中其他的属性
9.2.7 @JsonSerialize
于指定类、字段或方法在序列化过程中使用的自定义序列化逻辑
① 创建一个包含 @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方法
}
复制代码
②使用示例
@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
9.3 反序列化注解
9.3.1 @JsonSetter
用于标记一个方法或字段,指示 Jackson 在反序列化过程中应如何设置该属性的值
①创建PersonSetter
类
public class PersonSetter {
@JsonSetter("first_name")
private String firstName;
@JsonSetter("last_name")
private String lastName;
// 其他getter setter toString方法省略...
}
复制代码
②使用示例
@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 对象的属性
9.3.2 @JsonAnySetter
用于处理反序列化过程中遇到的未知或额外的 JSON 键值对。当一个对象的 JSON 表示中包含无法直接映射到已声明属性的键时,这个注解可以帮助捕获并存储这些额外的数据
① 定义带有 @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;
}
}
复制代码
②使用示例
@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 输入场景。
9.3.3 @JsonCreator
用于标记一个构造器、静态工厂方法或实例方法,使其成为反序列化过程中创建对象实例的入口点。这个注解帮助 Jackson 确定如何根据 JSON 数据构建相应的 Java 对象。
① 定义带有 @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方法省略...
}
复制代码
② 使用示例
@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=昆明)
复制代码
9.3.4 @JacksonInject
用于在反序列化过程中自动注入依赖项或附加信息到目标对象。通常用于处理那些不能直接从 JSON 数据中获取、但又希望在反序列化完成后立即可用的信息
① 定义带有 @JacksonInject
注解的类
public class UserInject {
private String name;
private String email;
// @JacksonInject注解在字段上,指示Jackson在反序列化过程中注入特定值
@JacksonInject("defaultEmail")
private String defaultEmail;
// 构造器和getter setter toString方法省略...
}
复制代码
②使用示例
@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 反序列化过程中用于引入外部依赖或默认值,使得这些信息能够在反序列化完成后立即可用
9.3.5 @JsonDeserialize
用于在反序列化过程中指定自定义的反序列化器来处理某个字段或类的特殊逻辑
①自定义反序列化器
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;
}
}
复制代码
② @JsonDeserialize
注解添加到要为其使用自定义反序列化器的字段
public class PersonDeserialize {
public long id;
public String name;
@JsonDeserialize(using = BooleanDeserializer.class)
public boolean enabled = false;
//构造器和getter setter toString方法省略...
}
复制代码
③使用示例
{"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
本期内容到此就结束了,希望对你有所帮助。我们下期再见 (●'◡'●)
文章转载自:xiezhr
原文链接:https://www.cnblogs.com/xiezhr/p/18119893
体验地址:http://www.jnpfsoft.com/?from=001
评论