
前言JSON 是现在很热门的数据传输格式。但是在数据传输格式上面并不是只有 JSON 这一种选择。Java 的序列化 IO 流可以实现 Java 对象和字节数组的相互转换从而在不同机器上传输 Java 对象。但是数据的转换依赖于 Java 的序列化 IO 流如果其他系统不是 Java 编写的如python、node.js 等它们可没有 Java 的 API可以接收但是无法解析数据。XML 文档基于自定义标签来保存并解释数据可读性好不好要具体情况具体分析而且全平台通用。但是 XML 文档占用的空间比较大因为标签需要闭合所以标签的结构是xxx/xxx这两个 xxx 是完全相同的冗余比较严重。与此相比JSON 更加轻量化一样是全平台通用可读性一样要具体问题具体分析在 js 或者 python 中已经提供了内置方法可以处理 JSON。那现在问题来了Java 这么热门活跃了这么多年一定也有内置的 API 可以处理 JSON 吧。很可惜在 Java SE 中并没有内置的 API 可以处理 JSONJava EE 倒是有。但是 Java EE 不是直接安装 JDK 就可以用的Java EE 其实就是在 Java SE 的基础上封装了一些依赖最基础的 Java SE 是不支持处理 JSON 的。所以如果我们要在 Java 中处理 JSON 的话有两种选择自己编写处理 JSON 的代码使用别人已经写好的处理 JSON 的代码OK啊那这里我也是直接忽略第一个选择了直接介绍第二个选择吧自己的编程技术还是囊中羞涩啊。Jackson 就是一个可以处理 JSON 的库函数并且 Spring 默认的 JSON 解析器就是 Jackson也就是在 Spring 项目中可以直接用 Jackson不需要引入其他处理 JSON 的库函数了。准备工作首先当然是引入依赖正如前文所说Spring 自带了 Jackson只要引入了 spring-boot-starter-web 或者 spring-boot-starter-webflux 就可以直接使用 Jackson 了。如果是非 Spring 项目需要引入以下依赖dependency groupIdtools.jackson.core/groupId artifactIdjackson-databind/artifactId version3.2.0/version /dependency这个版本比较新跟老版本的 API 有些是不一样的可以去 maven 仓库Maven Repository: Search/Browse/Explore 找一下其他的版本。当然 Spring Boot 的版本不同用到的依赖版本也不同。对象 - JSONpublic class Main { public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); User user new User(李华, 12345, 18, List.of(1, 2, 3, 4, 5), new User.School(广州大学, 广州)); String json objectMapper.writeValueAsString(user); // {age:18,list:[1,2,3,4,5],password:12345,school:{address:广州,name:广州大学},username:李华} System.out.println(json); } } NoArgsConstructor AllArgsConstructor Data class User { private String username; private String password; private Integer age; private ListInteger list; private School school; NoArgsConstructor AllArgsConstructor Data static class School { private String name; private String address; } }ObjectMapper 就是 Jackson 提供的入口类可以通过 ObjectMapper 的对象调用方法处理 JSON。在 ObjectMapper 中就有一个非常简单粗暴的方法public String writeValueAsString(Object value)将任何传入的对象转成 JSON 字符串并且可以自动处理对象之间的嵌套关系。但是这个方法也不是完美的在处理 Map 类型的时候有一些问题。public class Main { public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); HashMapUser, Integer map1 new HashMap(); HashMapInteger, User map2 new HashMap(); User user new User(李华, 12345, 18); map1.put(user, 1); map2.put(1, user); // {User(username李华, password12345, age18):1} System.out.println(objectMapper.writeValueAsString(map1)); // {1:{age:18,password:12345,username:李华}} System.out.println(objectMapper.writeValueAsString(map2)); } } NoArgsConstructor AllArgsConstructor Data class User { private String username; private String password; private Integer age; }因为 Map 和 JSON 都是键值对所以如果用 User 作为 key在转换的时候会调用 User 的 toString 方法得到 User 对象的字符串表示形式作为 key所以 key 是 User(username李华, password12345, age18)。如果 User 作为 value 就没有这种情况可以被转换成 {username:李华,password:12345,age:18}。其实这里的 Map 没有问题啊因为键值对的 key 都是 description描述性信息基本上是不会用对象作为 key 的。如果不是我要写博客考虑的情况多了一点我也不会发现这个情况说明在我之前的使用过程中根本没有碰到过用对象作为 key 的情况。我这里就以抖音的一个接口返回的 JSON 为例可以看到 key 都是描述性文字都是用 value 来表示对象的。JSON - 对象public class Main { public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); String json { username:李华, password:12345, age:18 } ; User user objectMapper.readValue(json, User.class); System.out.println(user.getUsername()); System.out.println(user.getPassword()); System.out.println(user.getAge()); HashMapString, Object map objectMapper.readValue(json, new TypeReferenceHashMapString, Object() { }); // password:12345 // age:18 // username:李华 for (Map.EntryString, Object entry : map.entrySet()) { System.out.println(entry.getKey() : entry.getValue()); } } } NoArgsConstructor AllArgsConstructor Data class User { private String username; private String password; private Integer age; }ObjectMapper 提供了 readValue 方法将字符串转成对象。如果是非泛型对象直接传入 对象.class 即可。如果是泛型对象比如这里的 HashMap就必须通过匿名内部类的方式 new TypeReferenceHashMapString, Object() {}。如果直接是 HashMap.class因为 Java 在编译期会进行泛型擦除所以返回的没有泛型的 HashMap 对象如下所示HashMap map objectMapper.readValue(json, HashMap.class); // 李华 System.out.println(map.get(username)); for (Object o : map.keySet()) { } for (Object o : map.entrySet()) { }如果是通过 get 方法去拿一样能拿到因为 get 方法的参数就是 Object 类型。但是 key 和 value 都变成 Object 类型了还要手动转换类型。而 new TypeReferenceHashMapString, Object() {}是通过匿名内部类记录了父类的泛型信息而类的定义中的泛型信息是不会被擦除的所以父类的泛型信息可以保留下来。也就是 new TypeReferenceHashMapString, Object() {} 记录了父类TypeReferenceHashMapString, Object 的泛型信息可以自动进行转换。直接解析 JSON虽然可以将 JSON 转成对象但是如果是深度嵌套的 JSON 呢为一个深度嵌套的 JSON 编写一个同样层级的 Java 类也是非常麻烦的。所以直接祭出最无敌的打法直接解析 JSON 结构。ObjectMapper objectMapper new ObjectMapper(); String json { username:李华, password:12345, age:18 } ; JsonNode jsonNode objectMapper.readTree(json); String username jsonNode.path(username).asString(); String password jsonNode.path(password).asString(); int age jsonNode.path(age).asInt();通过 readTree 方法解析 JSON 结构通过 path 方法传入 key 获取 value并且可以通过 asXxx 方法对获得的值进行类型转换。不过上面的示例只是基础用法下面还有进阶用法。readTree 其实就是把 JSON 结构解析成树状结构Jackson 根据 JSON 的特点将树节点分成三种类型数组节点、对象节点、数值节点。ObjectMapper objectMapper new ObjectMapper(); String json [ { username:李1华, password:12345, age:18 }, { username:李2华, password:12345, age:18 }, { username:李3华, password:12345, age:18 } ] ; ArrayNode arrayNode (ArrayNode) objectMapper.readTree(json); for (JsonNode node : arrayNode) { ObjectNode objectNode (ObjectNode) node; System.out.println(objectNode.path(username).asString()); }readTree 和 path 返回的都是 JsonNode需要强转成 ArrayNode 和 ObjectNode。从这个关系就可以看出来JsonNode 是 ArrayNode 和 ObjectNode 的父类。ArrayNode 就是数组嘛所以可以直接遍历里面的所有元素而 ObjectNode 就是一个对象里面有一堆键值对嘛通过 path 传入键的名字就可以获得对应的值。path 返回的可能还是对象或者是叶子节点也就是那几种基本数据类型字符串、整数、小数、布尔值如果是 path 下面没有其他嵌套的对象了那返回的就是数值节点。JsonNode 是一个抽象类它下面的子类有ArrayNode 表示数组、ObjectNode 表示对象、而 BooleanNode/NullNode 这些节点表示数值。如果是 ArrayNode就可以用 path(0) 选中其中第一个元素。如果是 ObjectNode就可以用 path(username) 获取对象中的属性。当然如果已经了解 JSON 的结构就不需要这么麻烦。{ id: 81c65c60-e25c-4f6b-a848-af81f813d3e1, object: chat.completion, created: 1784880697, model: deepseek-v4-flash, choices: [ { index: 0, message: { role: assistant, content: 你好我是DeepSeek由深度求索公司创造的智能助手。我能够处理各种问题无论是文本对话、文件上传还是阅读链接内容我都可以胜任。目前我专注于文字处理不支持多模态识别但可以对上传的图片中的文字进行读取和理解。我的知识截止日期是2025年5月并且我支持联网搜索功能需要手动开启。最重要的是我完全免费使用有什么可以帮你的吗, reasoning_content: 嗯用户问了一个简单的自我介绍问题。这个问题很直接就是想知道我的身份和功能。我需要清晰说明自己是什么、能做什么。\n\n我是DeepSeek由深度求索公司创造的AI助手。应该简明扼要地介绍核心特点纯文本对话、文件处理能力、免费使用、有知识截止日期和上下文长度限制。同时可以用友好的语气表达愿意提供帮助的态度。\n\n想到了用\你好\开头然后说明身份和基本能力最后以开放式的提问结束这样既回答了问题又促进了进一步交流。 }, logprobs: null, finish_reason: stop } ], usage: { prompt_tokens: 10, completion_tokens: 211, total_tokens: 221, prompt_tokens_details: { cached_tokens: 0 }, completion_tokens_details: { reasoning_tokens: 112 }, prompt_cache_hit_tokens: 0, prompt_cache_miss_tokens: 10 }, system_fingerprint: fp_8b330d02d0_prod0820_fp8_kvcache_20260402 }从这个 JSON 中获取 content 属性JsonNode node objectMapper.readTree(json); node.path(choices).path(0).path(message).path(content).asString();还有一件事属性缺失和属性溢出public class Main { public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); String json1 {username:李华} ; String json2 {username:李华,school:广州大学} ; User user1 objectMapper.readValue(json1, User.class); User user2 objectMapper.readValue(json2, User.class); // User(username李华, passwordnull, agenull) // User(username李华, passwordnull, agenull) System.out.println(user1); System.out.println(user2); } } NoArgsConstructor AllArgsConstructor Data class User { private String username; private String password; private Integer age; }如果 JSON 的属性和类的属性没有对齐还是可以转换的。transient 关键字Java 的序列化 IO 流可以通过 transient 关键字指定某个属性不被序列化Jackson 可以吗Jackson 提供了 JsonIgnore 和 JsonIgnoreProperties 注解来指定属性不被序列化。NoArgsConstructor AllArgsConstructor Data class User { private String username; private String password; JsonIgnore private Integer age; }age 属性不会被序列化得到的结果是{password:12345,username:李华}。NoArgsConstructor AllArgsConstructor Data JsonIgnoreProperties({age, password}) class User { private String username; private String password; private Integer age; }也可以通过 JsonIgnoreProperties 把不需要序列化的属性一锅端得到的结果是{username:李华}。我要变美ObjectMapper objectMapper new ObjectMapper(); ArrayListUser list new ArrayList(); list.add(new User(李1华, 12345, 18)); list.add(new User(李2华, 12345, 18)); list.add(new User(李3华, 12345, 18)); String json objectMapper.writeValueAsString(list); System.out.println(json);集合转成 JSON 之后打印出来的效果是这样的。这些对象挤在一起好难看啊。爱美之心人皆有之怎么才能让 JSON 变美呢JsonNode 有一个 toPrettyString 方法可以让 JSON 以更美观的形式打印出来。ObjectMapper objectMapper new ObjectMapper(); ArrayListUser list new ArrayList(); list.add(new User(李1华, 12345, 18)); list.add(new User(李2华, 12345, 18)); list.add(new User(李3华, 12345, 18)); String json objectMapper.writeValueAsString(list); System.out.println(objectMapper.readTree(json).toPrettyString());空悲切如果 JSON 没有这个属性但是我就是 path 想拿到这个属性会怎么样呢public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); String json [{username:李华,school:广州大学}] ; JsonNode node1 objectMapper.readTree(json).path(1); String age objectMapper.readTree(json).path(1).path(age).asString(); if (node1.isEmpty()) System.out.println(111); }首先JsonNode 有 isEmpty 方法可以用来判断是否为空。那如果我很急我没有写判断语句呢就像 String age objectMapper.readTree(json).path(1).path(age).asString() 这行代码一样path(1) 是拿不到数据的而且也没有 age 字段那这个 age 的取值会是什么呢debug 可以看到age 所以其实 Jackson 已经帮你做了善后不然获取不存在的属性应该是会报错的。但是我对这个结果并不满意这也太不严谨了如果这个键值对就是 key: 呢我怎么知道它是有这个属性但是 value 还是没有这个属性呢asString 可以传入默认值如果没有找到这个属性就使用默认值。public static void main(String[] args) { ObjectMapper objectMapper new ObjectMapper(); String json [{username:李华,school:广州大学}] ; String age objectMapper.readTree(json).path(1).path(age).asString(null); if (age null) System.out.println(没有找到age); }