作者 :孙玉昌,昵称【一一哥 】,另外【壹壹哥】也是我哦
CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者
前言
在上一篇文章中,壹哥 给大家讲解了XML及其解析相关的内容,让我们了解到了XML的优缺点。实际上,因为XML文档的解析复杂性,加上对内存消耗过多,存储同样的内容,XML需要占用较多的内存和网络资源,所以目前很多项目中都已经不再使用XML了,取而代之的是JSON。今天壹哥会再利用一篇文章,来给大家讲解JSON及其解析技术,希望你能喜欢哦。
在开讲之前,壹哥 必须提醒大家,JSON解析很常用,大家必须熟练掌握!!!熟练掌握!!!熟练掌握!!!
------------------------------前戏已做完,精彩即开始----------------------------
全文大约【5700】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......
配套开源项目资料
Github: github.com/SunLtd/Lear...
Gitee: gitee.com/sunyiyi/Lea...
一. JSON简介
1. 概念
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它算是JavaScript语言的一部分,与XML一样都可以用于数据的存储和传输。一个JSON文档可以由JSON对象和JSON数组两部分组成,内部的格式由键值对组成。在实际应用中,我们经常会把JSON数组和JSON对象组合起来构建成复杂的数据结构,以满足不同的需求。
2. 优缺点
JSON这种信息载体,相比XML来说,具有自己鲜明的特点,具体来说,就是具有如下优缺点。
2.1 JSON的优点
- 易于读写:JSON的格式非常简单,易于读写和维护;
- 易于解析:JSON数据结构简单,解析速度快,可以很方便地被各种编程语言所解析;
- 易于传输:JSON数据量小,传输速度快,非常适合于网络传输;
- 易于扩展:JSON支持嵌套,可以很方便地添加新的数据结构;
- 与JavaScript兼容:JSON是JavaScript的一个子集,可以很方便地被JavaScript解析。
2.2 JSON的缺点
但是JSON也不是说就十全十美,同样也有一些小缺点。
- 无注释:JSON数据中不支持注释,这使得JSON文件的可读性变得较差;
- 无标准:JSON没有统一的标准定义,各种实现之间有一定的差异;
- 不支持二进制数据:JSON只支持文本数据,不支持二进制数据,这在一些场景中可能会受到限制;
- 可读性差:由于JSON的格式比较简单,因此在一些复杂的数据结构中,可能会出现可读性差的情况。
但总的来说,瑕不掩瑜,JSON作为一种非常方便、易于使用的数据格式,具有良好的跨平台性和易扩展性,常用于前后端数据交互、移动应用开发、IoT应用等场景中。
3. JSON与XML对比
正是基于以上特点,所以在现代开发中,JSON比XML更常用 。毕竟JSON具有更简单、更清晰的语法和结构,同时也比XML更轻量级、更易于解析和处理。此外,JSON也更适合于Web应用程序和移动应用程序,因为它可以更快地在客户端和服务器之间传输数据。当然在某些特殊的应用场景中,如数据交换和存储等,XML仍然是一种重要的数据格式。请大家来看看壹哥做的JSON与XML对比总结,如下表所示:
**** | JSON | XML |
---|---|---|
格式 | 轻量、易读 | 冗长、复杂 |
数据类型 | 数组、对象、字符串、数字、布尔 | 数组、对象、字符串、数字、布尔 |
解析库 | Jackson、Gson | DOM、SAX、JAXB |
可读性 | 易于阅读和理解 | 需要熟悉格式和标签 |
应用场景 | 前后端数据交互、API数据传输 | 数据存储、传输、配置 |
大家可以根据自己的实际需要,选择合适的格式进行数据的存储和传输。
3. 使用场景
我们现在知道,JSON作为一种轻量级的数据交换格式,具有易读性、易于编写和解析的特点,因此经常用于前后端数据交互。具体来说,经常会用于以下这些场景中:
- Web开发中,前后端数据交互。
- 移动应用开发中,客户端与服务器数据交互。
- IoT应用中,设备与云端数据交互。
- 大数据应用中,数据交换和存储。
- 日志处理中,日志数据的存储和分析。
- 软件测试中,数据的生成和验证。
- 其他各种数据交换场景。
除了以上这些应用之外,当然还有很多其他的使用场景,在此壹哥就不再细说了。
二. JSON语法
1. 基本构成
JSON文档有 JSON对象和JSON数组 两种基本类型, 这两种基本类型又主要由以下几个部分组成:
- 对象(Object) :由一组键值对组成,使用大括号({})包含,键值对之间使用逗号(,)分隔;
- 数组(Array) :由一组有序的值组成,使用中括号([])包含,值之间使用逗号(,)分隔;
- 值(Value) :由一组有序的值组成,使用中括号([])包含,值之间使用逗号(,)分隔;
- 键(Key) :一个字符串,用于表示对象中的一个键值对的键;
- 字符串(String) :由一组字符组成。在JSON中,字符串必须使用双引号("")包含;
- 数字(Number) :一个整数或浮点数;
- 布尔值(Boolean) :true或false;
- 空值(Null) :表示一个空值,只有一个关键字null。
其中,JSON对象要使用 大括号{}
包裹起来 ,在大括号{}内部可以包含多个键值对。每个键值对由一个键和一个值组成,键和值之间使用冒号:
分隔,而多个键值对之间要使用逗号,
分隔。键值对的键是一个字符串,值可以是任意类型的数据,可以是数字、字符串、布尔值、数组或其他JSON对象。如下所示:
json
{
"name": "一一哥",
"age": 25,
"email": "yyg@example.com"
}
上面的JSON对象有三个键值对,分别是name、age和email,它们的值分别是字符串、数字和字符串类型。
而JSON数组可以看作是一组有序的值,整个数组要使用 中括号[]
包裹起来 。在JSON数组中可以包裹其他的JSON对象或数组,进行多重嵌套。数组中的每个值也都可以是任意类型的数据(包括字符串、数字、对象、数组等),值之间同样使用逗号,
分隔。如下所示:
json
[
"apple",
"banana",
"cherry",
{
"name": "一一哥",
"age": 30
},
[1, 2, 3]
]
上面的JSON数组有五个值,分别是三个字符串、一个对象和一个数组。其中,第四个值是一个JSON对象,第五个值是一个JSON数组。
虽然JSON对象和JSON数组在语法上有所不同,但它们都是用来表示JSON数据的常用结构。在实际应用中,我们可以将它们组合嵌套起来构建出更加复杂的数据结构,以满足不同的需求。比如下面这个JSON文档:
json
{
"name": "一一哥",
"age": 25,
"email": "yyg@126.com",
"hobbies": [
"吃饭饭",
"音乐",
{
"name": "钢琴",
"level": 3
}
]
}
在上面这个文档中,壹哥定义了一个包含四个键值对的JSON对象,其中name、age和email是字符串类型的key。hobbies是一个包含三个元素的JSON数组,其中前两个元素是字符串,第三个元素又是一个JSON对象,包含name和level两个键值对。注意,在JSON数组中可以包含任意类型的元素,包括字符串、数字、对象、数组等。
为了让大家更好地搞清楚JSON语法的构成,接下来壹哥再分别给大家讲解一下JSON语法中的各个组成部分。
2. 对象
JSON对象是一组由键值对组成的无序集合,用大括号{}
包裹。每个键值对之间使用逗号,
分隔。键是一个字符串,值可以是任意类型的数据,包括对象、数组、字符串、数字、布尔值和空值。我们来看下面这个包含了三个键值对的JSON对象:
json
{
"name": "一一哥",
"age": 20,
"city": "上海"
}
3. 数组
JSON数组是一组有序的值的集合,用中括号[]
包裹。每个值之间使用逗号,
分隔,值可以是任意类型的数据,包括对象、数组、字符串、数字、布尔值和空值。我们来看下面这个包含了三个值的JSON数组:
json
["apple", "banana", "cherry"]
4. 值
JSON的值可以是任意类型的数据,包括对象、数组、字符串、数字、布尔值和空值。例如:
json
{
"name": "一一哥",
"age": 20,
"isMarried": false,
"hobbies": ["读书", "运动", "音乐"],
"address": {
"street": "上海同济支路199号",
"city": "上海",
"zipcode": "10001"
},
"phoneNumbers": [
{
"type": "home",
"number": "555-1234"
},
{
"type": "work",
"number": "555-5678"
}
]
}
上面的JSON对象中包含了字符串、数字、布尔值、数组和对象等多种类型的值。
5. 键
JSON对象中的键必须是字符串类型,要用双引号""
包裹。键应该是唯一的,重复的键会被覆盖。例如:
json
{
"name": "一一哥",
"age": 30,
"city": "上海"
}
6. 字符串
JSON字符串是由双引号""
包含的任意Unicode字符序列,字符串中可以包含转义字符,如下表所示:
转义字符 | 描述 |
---|---|
\" | 双引号 |
\\ | 反斜杠 |
\/ | 正斜杠 |
\b | 退格符 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\t | 水平制表符(tab) |
\uXXXX | Unicode代码(XXXX表示四位数字) |
例如:
json
{
"name": "一一哥",
"city": "上海",
"address": "123 Main Street\\nApt 4B"
}
7. 数字
JSON中的数字可以是整数或浮点数,可以带正负号和小数点。例如:
json
{
"age": 30,
"price": 12.99,
"temperature": -5.6
}
8. 布尔值
JSON中的布尔值只有两个取值,即true和false。例如:
json
{
"isMarried": true,
"hasChildren": false
}
9. 空值
JSON中的空值表示为null。例如:
json
{
"name": "一一哥",
"address": null
}
在JSON解析时,我们可以将null转换为Java中的null值,表示缺少数据。
熟悉了JSON的基本语法之后,壹哥再来带大家学习如何实现JSON解析,该部分的内容主要包括将一个Java对象转为JSON字符串,和把JSON字符串转为对应的Java对象。
三. JSON解析
1. 概述
我们先来看看什么是JSON解析。
JSON解析就是把使用JSON格式编写的数据,转换为计算机程序可以使用的数据类型。在Java中,有许多JSON解析库可供我们进行使用,比如Jackson、FastJSON、Gso n 等。接下来壹哥主要是结合Java推荐的Jackson来给大家介绍JSON解析的概念、原理和具体实现方法。
2. JSON解析原理
在我们开始进行JSON解析之前,我们先来看看解析的实现原理。
JSON解析的实现原理其实就是将JSON字符串转换为Java对象,或反过来将Java对象转换为JSON字符串 。其中,将Java对象转换为JSON字符串的过程称为序列化;反之,将JSON字符串转换为Java对象的过程称为反序列化 。在Java中,我们通常是使用反射技术来实现JSON解析,即根据JSON字符串中的数据类型,利用Java的反射机制动态地创建出对应的Java对象,并将JSON字符串中的数据赋值给Java对象的各个属性。
3. Jackson简介
3.1 概述
Jackson是一个流行的Java JSON解析库,可以将JSON字符串转换为Java对象,也可以将Java对象转换为JSON字符串 。它支持流式API、数据绑定和树模型等多种解析方式,同时还给我们提供了许多注解,用于控制JSON序列化和反序列化。具有性能高、使用简单等优点。
3.2 常用API
我们在利用Jackson开发时,肯定会用到ObjectMapper类,该类是Jackson框架中最核心的类之一,它可以将Java对象转换为JSON字符串,或反过来将JSON字符串转换为Java对象。以下是ObjectMapper类的一些常用方法:
- readValue:将JSON字符串转换为Java对象;
- writeValueAsString:将Java对象转换为JSON字符串;
- writeValue:将Java对象写入输出流中。
这几个方法需要大家牢牢记住,除此之外,Jackson中还有以下几个常用的注解:
- @JsonProperty:用于在JSON属性名和Java属性名之间建立起映射关系;
- @JsonFormat:用于指定日期类型的格式;
- @JsonIgnore:用于指定某个属性,不参与序列化和反序列化;
- @JsonInclude:用于指定某个属性的条件序列化和反序列化。
3.3 核心依赖
由于Jackson并不是Java本身自带的API,所以如果我们想在Java中使用Jackson,就需要导入相关的依赖。如果我们是在Maven项目中,可以通过如下坐标导入依赖:
xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.3</version>
</dependency>
其中,jackson-core和jackson-databind是Jackson最常用的模块,里面包含了JSON序列化和反序列化的核心功能。我们还可以根据需要导入其他的Jackson模块,如jackson-annotations等。因为我们现在还没有学习Maven,所以只能手动导入相关的依赖了。在导入依赖之后,我们就可以开始使用Jackson进行JSON解析了,请大家继续往下看。
3. 序列化
我们在进行前后端数据交互时,前端通常是使用JSON格式来传递数据,后端则需要将这些JSON格式的数据转换为Java对象进行处理。同时,后端也需要将Java对象转换为JSON格式,以便前端使用。所以前后端之间就需要进行不同格式之间的转换,这就是序列化和反序列化。壹哥 在前面讲过,序列化就是将Java对象转换为JSON字符串的过程 。
3.1 对象转字符串
我们先来看一个简单的序列化代码,将一个Java对象转为json字符串,如下所示:
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author 一一哥Sun
*/
public class Demo01 {
public static void main(String[] args) {
try {
//创建一个Person对象
Person person=new Person();
person.setName("一一哥");
person.setAge(20);
person.setAddress("上海校区");
//创建一个ObjectMapper对象映射对象
ObjectMapper mapper = new ObjectMapper();
//序列化:将Java对象转为json字符串
String json = mapper.writeValueAsString(person);
System.out.println("json="+json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
执行结果如下图所示:
3.2 集合转字符串
除了可以将单个Java对象转为JSON字符串之外,我们还可以将Java数组、集合等复杂对象类型转为JSON字符串,实现Java对象与JSON数组之间的转换。代码如下所示:
java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author 一一哥Sun
*/
public class Demo03 {
public static void main(String[] args) {
try {
//创建一个集合
Map<String,List<Person>> map=new HashMap<>();
List<Person> list=new ArrayList<>();
Person p1=new Person();
p1.setName("一一哥");
p1.setAge(20);
p1.setAddress("山东");
Person p2=new Person();
p2.setName("壹哥");
p2.setAge(18);
p2.setAddress("北京校区");
Person p3=new Person();
p3.setName("孙老师");
p3.setAge(30);
p3.setAddress("青岛校区");
list.add(p1);
list.add(p2);
list.add(p3);
map.put("persons", list);
//创建一个ObjectMapper对象映射对象
ObjectMapper mapper = new ObjectMapper();
//序列化:将Java对象转为json字符串
String json = mapper.writeValueAsString(map);
System.out.println("json="+json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
执行结果如下图所示:
4. 反序列化
4.1 字符串转对象
而反序列化,就是将JSON字符串转换为Java对象的过程。我们来看看如下代码:
java
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author 一一哥Sun
*/
public class Demo02 {
public static void main(String[] args) {
//先创建一个ObjectMapper对象映射对象
ObjectMapper mapper = new ObjectMapper();
//允许json字符串中有单引号,解决JsonParseException:
//Unexpected character (''' (code 39)): was expecting double-quote to start field name
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
String jsonString = "{'name':'一一哥','age':18,'address':'上海'}";
try {
//反序列化:将JSON字符串转换为对应的Java对象
Person person = mapper.readValue(jsonString, Person.class);
System.out.println(person.getName()+"--"+person.getAge()+"--"+person.getAddress());
} catch (JsonProcessingException e) {
//处理json处理异常
e.printStackTrace();
}
}
}
执行结果如下图所示:
在上面的案例中,我们首先创建了一个ObjectMapper对象,然后使用readValue方法将JSON字符串转换为Person对象。Person类需要拥有对应的属性和getter/setter方法,最后输出了person对象的name和age等属性。
4.2 字符串转集合
除了可以将JSON字符串转为单个对象,我们也可以将一个复杂的JSON字符串转为一个合适的集合对象类型。
java
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author 一一哥Sun
*/
public class Demo04 {
public static void main(String[] args) {
// 先创建一个ObjectMapper对象映射对象
ObjectMapper mapper = new ObjectMapper();
// 允许json字符串中有单引号,解决JsonParseException:
// Unexpected character (''' (code 39)): was expecting double-quote to start
// field name
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
String jsonString = "{'persons':[{'name':'一一哥','age':20,'address':'上海校区'},{'name':'壹哥','age':18,'address':'北京校区'}]}";
try {
// 反序列化:将JSON字符串转换为对应的Java对象
//直接将json字符串转为对应的Java对象
Persons persons = mapper.readValue(jsonString, Persons.class);
List<Person> list = persons.getPersons();
for (Person person : list) {
System.out.println(person.getName() + "--" + person.getAge() + "--" + person.getAddress());
}
// 也可以直接将json字符串转为Map集合
Map<String, Person> map = mapper.readValue(jsonString, Map.class);
for (Map.Entry<String, Person> entry : map.entrySet()) {
System.out.println(entry.toString());
}
} catch (JsonProcessingException e) {
// 处理json处理异常
e.printStackTrace();
}
}
}
执行结果如下图所示:
在上面的案例中,我们创建了一个Persons类,内部包含了一个包含Person对象的List列表。然后使用ObjectMapper对象将JSON字符串转换为Person对象,最后遍历PersonList中的每个Person对象,输出其属性值。这个案例中,JSON字符串中的name、age和address属性,分别对应了Person类中的name、age和address属性。
我们就是通过以上这几种方式,实现了Java对象与JSON字符串之间的转换,你学会了吗?
5. 注意事项
我们在进行JSON解析时,需要注意以下几个问题:
- JSON字符串必须是有效的JSON格式,否则会导致解析失败;
- Java类中的属性的名称和类型,必须与JSON对象或JSON数组中的键和值相对应;
- JSON字符串中的键必须是字符串类型,用双引号包裹;
- Java类中的属性类型必须与JSON字符串中的值类型相对应;
- 使用Jackson进行JSON解析时,需要将JSON字符串的Unicode转义符进行转义,例如将"\"转换为"\\";
- JSON字符串中的键必须是唯一的,重复的键会被覆盖;
- JSON字符串中的数组必须使用中括号包裹,并使用逗号分隔数组元素。
以上这些注意事项,大家一定要认真对待,否则可能会导致你出现一些莫名其妙的错误哦。
------------------------------正片已结束,来根事后烟----------------------------
四. 结语
壹哥 在本文给大家详细介绍了JSON解析的概念、原理和使用方法,并以Jackson为例进行了详细的实现。壹哥希望大家熟练掌握JSON解析,因为这个技术实在是太常用了哦。在实际开发中,我们要根据实际需求选择合适的JSON解析方式,并根据具体的情况选择相应的注解,以便让JSON解析更加得便捷和高效。
另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。