JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,它基于 JavaScript 的一个子集,易于人的编写和阅读,也易于机器解析。 JSON 采用完全独立于语言的文本格式,但是也使用了类似于 C 语言家族的习惯(包括 C, C++, C#, Java, JavaScript, Perl, Python 等)。这些特性使 JSON 成为理想的数据交换语言。
JSON 的诞生
由于互联网应用之间需要传输数据,且很多跨平台的程序需要交互,只能采取纯文本方式的交互,而 xml 当初就是一个选择,但是 xml 规范越来越多也越来越复杂,解析效率也比较低,很多攻城狮看到 xml 头都大了,可能搞了很久也搞不明白。
然后独钟于 JavaScript 的 JavaScript 大宗师 Douglas Crockford 根据 JavaScript 的规范发明并推广了 json,json 格式简单易用,且同样可跨平台传出,得到广泛的认可和传播。就这样,json 就越来越流行了,现在已经成为主流技术之一。
JSON vs XML
谈起 json,那 xml 肯定是少不了对比的东西,没有对比就是没有伤害,在和 json 履行相同职责的文本传输交换格式还有 json 的老大哥 xml(可扩展标记语言),在 json 横空出世以前,咱们用的可都是 xml 进行文件数据传输。
首先咱们要从定义上来看看 json 和 xml 的区别:
JSON(JavaScript Object Notation)
一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。可在不同平台之间进行数据交换。其语言习惯具备类 C 的习惯体系(C,C++,Java 等)。
XML(Extensible Markup Language,可扩展标记语言)
用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。
json 的横空出世,是充分吸取借鉴了 xml 的优点,故 json 和 xml 有着一些相同的优点:
- 可读性好,结构清晰
- 分层存储(层次嵌套)
- 都可作为 Ajax 传输数据
- 都跨平台,可作为数据传输格式
但毕竟 json 青出于蓝而青于蓝,肯定有着 xml 一些没有的特点和优势,例如:
- 数据格式简单,易读易写,且数据都是压缩的,文件较小,便于传输
- json 解析难度较低;而 xml 需要循环遍历 DOM 进行解析,效率较低
- 服务端和客户端可以直接使用 json,便于维护;而不同客户端解析 xml 可能使用不同方法
json 已成为当前服务器与 web 应用之间数据传输的公认标准,尽管如此,xml 仍有它独有应用领域:
- xml 格式较为严谨,可读性更强,更易于拓展,可以作为良好的配置文件
- 出现较早,在各个领域有广泛的应用,具有普遍的流行性
当然,不是所有的 json 都是”特仑苏”,适合场景需要的才是最好的。但在 web 领域的数据传输,它就是王者!
小小翻译官,JSON 的应用
json 之所以很流行,是因为 json 有着广泛的应用领域。主要包括与 AJAX 结合使用的统一平台的前端传输;跨平台的数据传输;非关系型数据库的文档存储等。这些领域通过使用 json 使得应用效率大大提高。
前端 AJAX+JSON 实现异步传输
json 本身就起源于 JavaScript,JavaScript 解析处理 json 有天然的优势,而在 Ajax异步加载 的数据中,整体页面已经加载过了,我们只需要在对应位置渲染真实的数据就行了,而这部分的真实数据我们用 json 文本来存储,使用 JavaScript 异步向服务端请求,请求成功之后 JavaScript 对其渲染填充就可以了。下图就是对前后端交互传统方式和ajax异步交互的简要描述:
对 AJAX也不熟悉?流程也看不懂?也不能明白异步传输的真谛在哪里?
那好咱们以下图这个例子来解释一下,对于一个非常庞大的网页部分,可以有各个模块组成,其中评论模块是我们非常小的模块,但是评论可能涉及很多条可能涉及分页,如果我们每次为了看下一页的评论,点击下一页就向服务端请求整个页面资源进行刷新,那样就太浪费服务端资源和带宽了(就评论的文本变了,就要把其他模块全部渲染一遍?)
所以我们采取所谓 AJAX 异步更新这个东西,也就是通过 JavaScript 请求下一页的评论相关数据(用 json 作为数据交互文本),JavaScript 得到这串 json 字符串中就有页面需要的评论信息,然后我们强大到无所不能的 JavaScript 将这部分重现渲染到评论模块的对应位置。
这个流程下来,我们不仅节约了带宽,提高了响应速度,还提高了服务器相对负载能力(每次请求需要的资源更少),提高了用户的使用体验,还提高了…(此处省略万字)
AJAX + JSON 实操
四个 Ajax 请求方法:
方法 | 描述 |
---|---|
$.ajax() |
执行异步 AJAX 请求 |
$.get() |
使用 AJAX 的 HTTP GET 请求从服务器加载数据 |
$.post() |
使用 AJAX 的 HTTP POST 请求从服务器加载数据 |
$.getJSON() |
使用 HTTP GET 请求从服务器加载 JSON 编码的数据 |
表单序列化方法:
方法 | 描述 |
---|---|
serialize() |
编码表单元素集为字符串以便提交 |
$.ajax()
ajax 请求参数 | 描述 |
---|---|
url | 请求地址 |
type | 请求方式(get 或 post) |
data | 发送给服务器的数据 两种表达方式: 1. "name=value&nama=value" 2. {key:"value",key:"value"} |
success | 回调函数 |
dataType | 服务器响应的数据类型:text、xml、json、script、html |
$.ajax({
url: "http://localhost:8080/JSON/ajaxServlet",
// data: "action=jQueryAjax",
data: {action: "jQueryAjax", key: "key值"},
type: "GET",
success: function (msg) {
var jsonObj = JSON.parse(msg);
$("#msg").html("jquery 编号:" + jsonObj.id + ", 姓名:" + jsonObj.name);
},
dataType: "text"
});
复制代码
$.get()
get 请求参数 | 描述 |
---|---|
url | 请求地址 |
data | 发送给服务器的数据 |
callback | 载入成功时的回调函数 |
dataType | 服务器响应的数据类型:text、xml、json、script、html |
示例:
$.get("http://localhost:8080/JSON/ajaxServlet", "action=jQueryGet",
function (msg) {
var jsonObj = JSON.parse(msg);
$("#msg").html("get 编号" + jsonObj.id + ", 姓名:" + jsonObj.name);
}, "text");
复制代码
$.post()
post 请求参数 | 描述 |
---|---|
url | 请求地址 |
data | 发送给服务器的数据 |
callback | 载入成功时的回调函数 |
dataType | 服务器响应的数据类型:text、xml、json、script、html |
示例:
$.post("http://localhost:8080/10_JSON_AJAX/ajaxServlet", "action=jQueryPost",
function (data) {
// var jsonObj = JSON.parse(data); // dataType为json!
$("#msg").html("post 编号" + data.id + ", 姓名:" + data.name);
}, "json");
复制代码
$.getJSON()
getJSON 请求参数 | 描述 |
---|---|
url | 请求地址 |
data | 发送给服务器的数据 |
callback | 载入成功时的回调函数 |
示例:
$.getJSON("http://localhost:8080/10_JSON_AJAX/ajaxServlet", "action=jQueryGetJSON",
function (msg) {
$("#msg").html("getJSON 编号:" + msg.id + ", 姓名:" + msg.name);
});
复制代码
serialize()
示例:
$.getJSON("http://localhost:8080/10_JSON_AJAX/ajaxServlet", "action=jQuerySerialize&" + $("form01").serialize(),
function (data) {
$("#msg").html("JSON_Serialize 编号:" + data.id + ", 姓名:" + data.name);
});
复制代码
跨平台 WebService
前面提到的是前后端的交互,前提是后端同是一门编程语言、平台,向前端提供服务。但随着互联网的发展,很多时候会遇到服务拆分、跨平台等服务的需要。而跨语言跨平台的最大障碍就是服务的交流问题。你总不至于用你电脑上的 C++ 代码直接调用我电脑上某个 Java 函数吧?为了解决这种问题,这时候通过 restful 风格的接口和 json 作为文本传输的格式就能良好的解决服务通信问题。
例如某个公司业务太大,将服务分配给 A 团队 和 B 团队。很多时候 A 可能需要进行调用 B 服务,如果 A 团队全部是 java,B 团队全部是 php,互相喊着天下第一不肯相让。这该怎么办?那么通过 json 进行通信是一种非常好的方式。流程如图简要所示:
非关系数据库存储(NoSQL)
随着互联网 web2.0 网站的兴起,传统关系数据库在处理超大规模网站和高并发方面显得有点力不从心。而非关系型的数据库由于它本身的特点得到非常迅速的发展,非关系数据库在大规模数据下也有非常良好的读写性能,且数据之间无关系,无形之间就在架构层面带来了可拓展的能力。
而有很多基于文档存储的非关系数据库采取 json 作为其存储格式,其中比较著名的有:MongoDB、CouchDB、RavenDB 等。存储的内容是文档型的,这样也有机会对某些字段建立索引,实现关系数据库的某些功能。
有些同学可能会问:既然 json 可以,那 xml 可以实现相似功能吗?
答案是不可以!因为像 xml 类型的字段,不管是查询还是更改效率都很一般,主要原因是 DB 层对 xml 字段很难建高效索引,应用层又要做从字符流到 dom 的解析转换。NoSQL 以 json 方式存储,提供了原生态的支持,在效率方面远远高于传统关系型数据库。
此外,Elasticsearch 等搜索引擎还用 json 和 Java API 提供其所有特性和功能。json 在开源中间件的应用也越来越多!
拒绝四不像,JSON 语法有要求
JSON 语法规则
json 语法是 JavaScript 语法的子集,而 json 一般也是用来传输对象和数组。也就是 json 语法是 JavaScript 语法的一部分(满足特定语法的 JavaScript 语法)。
- 数据保存在键值对中,数据由逗号分隔
- 花括号表示对象
- 中括号表示数组
JSON 名称/值
json 数据的书写格式为:"键":"值"
对应 JavaScript 的概念就是:键="值"
但 json 的格式和 JavaScript 对象格式还是有所区别:
- JavaScript 对象的名称可以不加引号,也可以单引号,也可以双引号,但 json 字符串的名称只能加双引号的字符表示
- JavaScript 对象的键值可以是除 json 值之外还可以是函数等其他类型数据,而 json 字符串的值对只能是数字、字符串(要双引号)、逻辑值、对象(加大括号)、数组(中括号)、null
JSON 对象
json 有两种表示结构——对象和数组,通过这两种表示结构可以表示更复杂的结构。
对比 java 的话,json 数组和 json 对象就好比 java 的列表/数组(Object 类型)和对象(Map)一样的关系。并且很多情况其对象值可能相互嵌套多层:对象中存在对象,对象中存在数组,数组中存在对象…
下面这张图能够一定程度反映 json 对象和数组的关系:
json 对象很容易理解,它代表一个实体,这个实体有一些其他的属性,这些属性可能是数字、字符串(要双引号)、逻辑值、对象(加大括号)、数组(中括号)、null。如果从 java 语言来看它就是对应一个实体类或者一个 Map,其中有一些用键值的方式描述名称和值。
遍历:可以用 for – in 进行对象遍历
修改:可以使用 .
或者 []
进行对象值的修改
删除:可以通过 delete 关键词删除 json 对象属性值
JSON 数组
学习完 json 对象,那么 json 数组的学习就容易的多了。json 数组与 json 对象有一些区别,json 数组用中括号表示([]
),各个值用逗号(,
)分隔,并且数组值需要是 json 合法数据类型(字符串,数字,对象,数组,布尔值或 null)。
// json数组
var names=["w","Q","YaoMing"];
// 对象套数组
var students=[
{"name":"w","height":175},
{"name":"Q","height":165},
{"name":"YaoMing","height":226}
];
复制代码
‘JavaScript 对象’ vs ‘JSON 对象’ vs ‘JSON 字符串’
在 JavaScript 中谈到 json,很多人会对 JavaScript 对象、json 对象、json 字符串混淆和概念不清晰,我们可以举个例子来一起看一下:
var a1={ name:"one",sex:"M"}; // JavaScript 对象
var a2={'name':'one','sex':'M'}; // JavaScript 对象
var a3={"name":"one","sex":"M"}; // 满足 json 格式的 JavaScript 对象
var a4='{"name":"one","sex":"M"}'; // json 字符串
复制代码
总的来说:
- JavaScript 对象:除了字符串、数字、true、false、null 和 undefined 之外,JavaScript 中的值都是对象
- json 对象:这个说法其实不太准确,没有单独的 json 对象,我们常说的 json 对象它实质是满足 json 格式要求的 JavaScript对象
- json 字符串:满足 json 语法格式的字符串(json是一种数据格式),有时也称 json 串
在这里多说几句,你可能会对 json 对象还是有点不够清晰,你可能在其他地方也会遇到 json 对象。首先,json 是一种数据格式,而 json 有对象和数组两种具体表现格式。
当你直接说 json 对象、json 数组的时候,它其实就是直接谈 json 的两种表示结构,它主要体现的是结构。
在 JavaScript 中,我们通常说的 json 对象、json 数组通常代指满足 json 格式的 JavaScript 对象、JavaScript数组。
在 java 中我们有时也说 json 对象、json 数组,这个其实就是第三方工具包基于 json 规范封装的 JSONObject、JSONArray 类。
统而言之:我们通常说的 json 对象、json 数组它实质是一种数据格式,但在不同语言中满足这种格式要求的对象、数组我们会称其为 json 对象、json 数组。
谷歌 Gson,精简而强大
序列化/反序列化介绍
前面我们学习了 json 的一些概念和基础语法,也知道了 json 起身于 JavaScript,在很多语言如 python 中得到较好的支持,但也有很多语言从语言本身来说是不支持 json 的(就比如咱们强大的java)。这虽然是一大障碍但并不阻止我们在 java 这门语言中使用 json。我们可以通过添加一些工具包使得 java 支持 json 处理。
这些工具包能够将 json 字符串转换成 java 对象,从而在 Java 中使用。反过来也可以将 java 对象转换成 json 字符串,从而更广泛地用在其他地方。将 Java 对象到 Json 字符串的过程我们称为 Serialization 序列化,将 Json 字符串转换成 Java 对象的过程我们称为 Deserialization 反序列化。
如果理解起来容易混淆,那么可以借助下面这张图进行结合记忆:咱们从 java 角度来看,java 对象需要从一个整体对象拆分成一个个碎片按序列往 json 字符串中写入,这就是一个序列化过程。而 json 字符串的一个个碎片反过来重新组装成一个完整的 java 对象这个过程就是反序列化。
对于 json 本身来说是不复杂的,但是在 java 中如果程序员直接操作 json 字符串那是一件非常麻烦和复杂的事情,不少优秀的程序员/团队/公司努力研究,将他们经验和智慧开源出来供大家使用,在其中,Gson / fastjson / Jackson 要更流行一些。咱们一个个了解一下~
Gson 介绍
在学习之前,你知道什么是 Gson 吗?
Gson 是谷歌开源的一个 Java 库,可用于将 Java对象转换为(序列化)其 JSON 表示形式。它还可以用于将 JSON 字符串转换为(反序列化)等效的 Java 对象。Gson 可以处理任意 Java 对象,包括没有源代码的现有对象。下图为 Gson 在 GitHub 主页一些信息:
每种 json 工具包都有它自己的优点和长处,对于 Gson 来说,有以下几点特点:
- 提供简单的
toJson()
和fromJson()
方法,将 Java 对象转换成 json 字符串,反之亦然 - 允许将现有的不可修改对象与 JSON 相互转换
- Java 泛型的广泛支持
- 允许对象的自定义表示
- 支持任意复杂的对象(具有深层次的继承层次结构、泛型等)
Gson 实战
下面和大家一起动手进行 Gson 实战,Gson 的功能比较强大,在这里呢和大家一起实现一些基础和常用的使用。
首先创建一个 java 项目(Maven), 要引入 Gson 的 Maven 依赖或 jar 包,其 Maven 依赖为:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
复制代码
有了 Gson 的依赖之后,那么实现 Java 对象与 Json 的转化也就很简单啦,大体就是分为两步:
- 首先创建 Gson 对象,这里可以直接 new 或者使用 GsonBuilder 进行创建,如果使用直接 new 的方式创建 Gson 对象是使用默认的配置;而使用 GsonBuilder 首先要创建 GsonBuilder,然后 GsonBuilder 调用一些配置方法,然后调用
create()
方法构建 Gson 对象 - 然后通过 Gson 对象的
toJson()
,fromJson()
方法进行序列化和反序列化操作
JavaBean 与 Json 字符串互相转换:
首先创建一个 Student 对象~
public class Student {
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
复制代码
其次,在测试中进行 JavaBean(student)
与 Json
字符串的转换。主要通过 toJson()
和 fromJson()
进行序列化和反序列化操作。
toJson,直译过来就是 “到达json”,所以是将 java 对象转成 json 字符串,也就是序列化。
fromJson,直译过来就是 “来自json”,所以是将 json 字符串转化为 java 对象,也就是反序列化。
//Gson gson= new GsonBuilder().create();//可以自定义一些配置
Gson gson=new Gson();//创建json对象
//java对象 ==> json
student stu=new student("Ben",22,"man");
String stustr=gson.toJson(stu,student.class);//json转为string
System.out.println("student对象为"+stu.toString());
System.out.println("转化为json字符串:"+stustr);
//json ==> java对象
///满足条件的json字符串{"name":"tony","age":32,"sex":"woman"}
String jsonstr="{\"name\":\"tony\"," +
"\"age\":32," +
"\"sex\":\"woman\"}";
student jsonstrobject=gson.fromJson(jsonstr,student.class);//转换为student对象
System.out.println("json字符串为"+jsonstr);
System.out.println("转化为student对象为:"+jsonstrobject.toString());
复制代码
执行结果:
java 集合与 json 字符串互相转化:
在实际开发中,我们很可能遇到的并不是 javaBean 与 json 字符串的直接转化,而是集合之类的转化工作,java 集合种类繁多。在此,我们实现 Map、List、String 数组的序列化和反序列化操作。
在进行序列化操作时,我们首先创建 Map<String,String>
,List<Object>
,String[]
对象然后填充一定数据以便进行序列化和反序列化操作。
Gson gson=new Gson();//创建json对象
// Map
Map<String,String> map=new HashMap<>();
map.put("one","1");
map.put("two","2");
// List
List<Object>list=new ArrayList<>();
list.add("hello");
list.add("world");
list.add(map);
// String[]
String[] str={"Hello","World"};
String mapTojsonStr=gson.toJson(map); // {"one":"1","two":"2"}
String listTojsonStr=gson.toJson(list); // ["hello","world",{"one":"1","two":"2"}]
String strTojsonStr=gson.toJson(str); // ["Hello","World"]
复制代码
我们将这些字符串复制到新的代码域进行反序列化操作,在反序列化时候,我们会用到 fromJson()
这个函数时,有两种我们常用的构造方式 fromJson(String json, Class<T> classOfT)
和 fromJson(String json, Type typeOfT)
,如果遇到泛型等类型时候需要借助 TypeToken
来获取对象类型:
Gson gson=new Gson();
String mapTojsonStr="{\"one\":\"1\",\"two\":\"2\"}"; // {"one":"1","two":"2"}
String listTojsonStr="[\"hello\",\"world\",{\"one\":\"1\",\"two\":\"2\"}]"; // ["hello","world",{"one":"1","two":"2"}]
String strTojsonStr="[\"Hello\",\"World\"]"; //["Hello","World"]
//方式一(这里避免冲突注释掉)
//Map<String,String>map1=gson.fromJson(mapTojsonStr,Map.class);
//方式二可以获取泛型等数据类型
Map<String,String>map1=gson.fromJson(mapTojsonStr,new TypeToken<Map<String,String>>(){}.getType());
List<Object>list=gson.fromJson(listTojsonStr,List.class);
Map<String,String>map2=(Map<String,String>)list.get(2);
String str[]=gson.fromJson(strTojsonStr,String[].class);2
复制代码
上面只是介绍了 java 对象与 json 字符串的转换,实际上 Gson 不仅入手容易,还有其他非常强大的功能,在使用 Gson 开发中除了 java 对象和 json 字符串的转换,我们经常也会对 JsonObject 直接进行操作(类似 JavaScript 中操作 json 串一样),这里你需要了解学习 Gson 封装的 JsonElement,JsonObject,JsonArray,JsonPrimitive,JsonNull 等数据类型。
不同的数据类型有各自的使用场景,下面给大家介绍下各个数据类型之间的区别与联系:
- JsonElement:表示 Json 元素的类。 它可以是 JsonObject,JsonArray,JsonPrimitive 或 JsonNull。这个你可以理解一下 java 中 List(Arraylist, LinkedList),Map(HashMap.TreeMap, ConcurrentHashMap)等联系。也可以理解为 Object 的类与其他类的关系。
- JsonObject:表示 Json 中对象类型的类。 对象由名称-值对组成,其中名称是字符串,而值是任何其他类型的 JsonElement。
- JsonArray:表示 Json 中数组类型的类。数组是 JsonElement 的列表,每个 JsonElement 的类型可以不同。这是一个有序列表,意味着保留添加元素的顺序。
- JsonPrimitive:表示 Json 基本值的类。 基本值可以是 String,Java 基本类型或 Java 基本类型的包装类。
- JsonNull:表示 Json 空值的类。
对于这些数据类型,你可能会问:为啥 json 字符串已经可以和 java 对象互相转了,还需要这些数据类型呢?
答案是这些数据类型让 java 中多一种可以处理 json 格式数据的方式。一方面让 java 处理 json 格式数据更加灵活,另一方面在某些场景下这样直接操作 JsonObject、JsonArray 等用以简化工作流程。
其实这些数据类型就是相当于用 java 的数据结构构造一个 json 的数据结构和方法(java 本身不直接支持 json),让我们能够直接使用和操作 json。
从上图可以看得出,上面这些数据结构也是根据 java 的一些数据结构作为存储,然后写一些操作函数,封装一些方法。当然,JsonObject 也可以通过 Gson 的 toJson()
和 fromJson()
方法灵活转成字符串和 java 对象。
有很多时候我们后台处理接受到的是一个 json 字符串,可能其内部结构也很复杂,如果我们将其转成 java 对象可能要编写 java 对应的实体,但是如果直接操作 json 对象可能就省下一些操作和流程,如果只需增删改其中很小的一部分,那么这种选择也是一种不错的方案。当然具体的使用方法和方案还需要根据具体的业务来判断!
飞人 fastjson,阿里”黑科技”
fastjson 介绍
除了谷歌的 Gson,咱们国内也有一款非常强大的 java 序列化工具包 — fastjson。下图为 fastjson 在 GitHub 的主页:
学习 fastjson 之前同样问:什么是 fastjson ?
- fastjson 是阿里巴巴的开源 JSON 解析库,它可以解析 JSON 格式的字符串,支持将 JavaBean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean
除了是国内阿里开源的,fastjson 还有优异的性能,fastjson 的优点如下:
- 速度快:fastjson 相对其他 JSON 库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越
- 使用广泛、测试完备:在阿里内部有广泛的应用
- 使用简单、功能完备:支持泛型、复杂类型等
fastjson 实战
下面带大家实战 fastjson,同样首先我们需要引入依赖,下载 jar 包引入或者 maven 的依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
复制代码
fastjson 与 Gson 虽然大体相似但有所区别,fastjson 自己也有实现的 JSONObject,JSONArray 类,前面在 Gson 中介绍过此类的作用我们在进行转换时候就把 JSONObject 加入进行转换。在 fastjson 主要提供以下三个类:
- JSON:fastJson 的解析器,用于 JSON 格式字符串与 JSON 对象及 JavaBean 之间的转换
- JSONObject:fastJson 提供的 json 对象
- JSONArray:fastJson 提供 json 数组对象
json 字符串、JSONObject 及 JavaBean 之间的相互转换
首先,我们同样定义一个 Student 类(同 Gson 的 Student 类)
// JavaBean ==> Json字符串、JSONObject
student student1 = new student("xiaoming",5,"man");
String jsonstr1 = JSON.toJSONString(student1); // JavaBean转Json字符串
JSONObject jsonObject1=(JSONObject) JSON.toJSON(student1); // JavaBean转JSONObject
System.out.println(jsonObject1.toString());
System.out.println(jsonstr1+"\n");
// Json字符串 ==> JavaBean、JSONObject
String jsonstr2="{\"age\":5,\"name\":\"xiaoming\",\"sex\":\"man\"}";
JSONObject jsonObject2 = JSON.parseObject(jsonstr2); // Json字符串转JSONObject对象
student student2 = JSON.parseObject(jsonstr2,student.class); // Json字符串转JavaBean
System.out.println(jsonObject2.toString());
System.out.println(student2.toString()+"\n");
//JSONObject ==> JavaBean、Json字符串
JSONObject jsonObject3=jsonObject2;
jsonObject3.put("age",18);
student student3=jsonObject3.toJavaObject(student.class); //JSONObject转JavaBean
String jsonstr3=jsonObject3.toJSONString(); //JSONObject转Json字符串
String name=jsonObject3.getString("name");
int age=jsonObject3.getInteger("age");
String sex=jsonObject3.getString("sex");
System.out.println("姓名"+name+" 年龄"+age+" 性别"+sex);
System.out.println(student3.toString());
System.out.println(jsonstr3+"\n");
复制代码
在测试代码中,我们分别编写一些代码实现三者的相互转换,但 JSONObject、JSONArrray、JSON 以及 fastjson 仍然有很多方法功能在这里就无法进行很详细的展示了:
// JavaBean ==> Json字符串、JSONObjectstudent student1 = new student("xiaoming",5,"man");String jsonstr1 = JSON.toJSONString(student1); // JavaBean转Json字符串JSONObject jsonObject1=(JSONObject) JSON.toJSON(student1); // JavaBean转JSONObjectSystem.out.println(jsonObject1.toString());System.out.println(jsonstr1+"\n");// Json字符串 ==> JavaBean、JSONObjectString jsonstr2="{\"age\":5,\"name\":\"xiaoming\",\"sex\":\"man\"}";JSONObject jsonObject2 = JSON.parseObject(jsonstr2); // Json字符串转JSONObject对象student student2 = JSON.parseObject(jsonstr2,student.class); // Json字符串转JavaBeanSystem.out.println(jsonObject2.toString());System.out.println(student2.toString()+"\n");//JSONObject ==> JavaBean、Json字符串JSONObject jsonObject3=jsonObject2;jsonObject3.put("age",18);student student3=jsonObject3.toJavaObject(student.class); //JSONObject转JavaBeanString jsonstr3=jsonObject3.toJSONString(); //JSONObject转Json字符串String name=jsonObject3.getString("name");int age=jsonObject3.getInteger("age");String sex=jsonObject3.getString("sex");System.out.println("姓名"+name+" 年龄"+age+" 性别"+sex);System.out.println(student3.toString());System.out.println(jsonstr3+"\n");
复制代码
对应输出的结果与预期一致:
json 字符串、JSONObject 及 Java 集合相互转换
上面进行了基于 JavaBean 的一些转换和操作,下面我们进行对 java 集合的一些转化实战。看看 fastjson 又是以什么样的参数进行的。java 的 Map 是常用集合之一,咱们先看看 Map 的相关转化:
// Map的相关转化
Map<String,String> map1=new HashMap<>();
map1.put("name","xiaoming");
map1.put("sex","woman");
String jsonstr=JSON.toJSONString(map1); //Map转json字符串
JSONObject jsonObject=(JSONObject) JSON.toJSON(map1); //Map转json对象
System.out.println(jsonstr);
System.out.println(jsonObject.toString());
// Map<String,String>map2=JSON.parseObject(jsonstr,Map.class);
//方式一
Map<String,String>map2=JSON.parseObject(jsonstr,new TypeReference<Map<String, String>>(){});
//方式二: json字符串转Map
Map<String,String>map3=jsonObject.toJavaObject(new TypeReference<Map<String, String>>(){});
System.out.println(map2.toString());
System.out.println(map3.toString());
复制代码
输出结果:
此外,List 同样也是 java 中使用较多的集合之一,我们可以看下它的相关转化:
// List相关转化
List<Map<String,String>> list1=new ArrayList<>();
Map<String,String> map1=new HashMap<>();
map1.put("name","map1");
map1.put("name","map2");
Map<String,String> map2=new HashMap<>();
map2.put("sex","man");
list1.add(map1);
list1.add(map2);
String jsonstr=JSON.toJSONString(list1); //list转json字符串
JSONArray jsonArray =(JSONArray) JSON.toJSON(list1); //list转jsonArray
JSONObject jsonObject=jsonArray.getJSONObject(0);
System.out.println(jsonstr);
System.out.println(jsonArray+" "+jsonArray.get(0));
// json 字符串转 List
List<Map<String,String>> list2=JSON.parseObject(jsonstr,new TypeReference<ArrayList<Map<String,String>>>(){});
//List<student> list3=JSON.parseArray("",student.class); //普通list的转换方式
System.out.println(list2.get(0).equals(map1)+" "+list2.get(1).equals(map2)); //如果相等则证明成功序列化
System.out.println(list2.toString());
复制代码
输出结果:
不难看出,fastjson 在入门还是非常简单的。并且和 Gson 有很多相似之处,在 API 的设计方面,Gson 需要一个 Gson 对象来进行相关操作,而 fastjson 的 JSON、JSONObject、JSONArray 定义了很多静态的方法可以直接使用。同时两者的反序列化的 TypeToken(Gson) 和 TypeReference(fastjson) 有异曲同工之妙。
这两者在开发过程中使用很多,各有优劣,并且这里只是里面很小的一部分内容,要想深入学习还需要了解官方全面 API 才行(Gson官方API,fastjson官方文档)。但是对于 fastjson 来说,有些地方可能存在一些漏洞和不稳定因素,但是阿里很快就进行修复。所以在实际使用中要考虑 fastjson 的安全性。
备受开源认可,Jackson 亦是真王者
Jackson 介绍
最后咱们介绍的就是当前更为成熟一点的 jackson。jackson 是一个能够将 java 对象序列化为 json 符串,也能够将 json 字符串反序列化为 java 对象的框架。下图为 jackson 在 GitHub 主页情况:
其实 jackson 的应用非常广泛,在这里我们简单了解一下 jackson 然后对比分析三个 json 工具包的异同,对于 jackson 来说,拥有以下几点优势:
- 出师较早,国际流行度较高,且社区学习内容较为丰富
- 安全稳定,是很多开源框架的内置序列化框架(例如:SpringMVC)
- 解析大的 json 文件效率较高,占用内存较少,性能比较好
- 拥有灵活的 API,可以很容易的拓展和定制
jackson 实战
下面带大家实战 jackson,在 Gson 和 fastjson 使用时只需引用一个 jar,而 jackson 却不是将所有全部集成到一个 jar 内,而是分成了多个不同的模块(不同模块具有不同功能),咱们首先引入 jackson 的依赖:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.0</version>
</dependency>
复制代码
有了 jackson 依赖之后,我们就可以进行实战了,在 jackson 中有三种方式操作 json:
- 流式 API – 使用 Stream(流)的方式对 Json 的每一个组成部分进行最细粒度的控制,JsonParser 读取数据,JsonGenerator 写入数据。(json streaming 流式计算,开销最低,读写最快)
- 树模型 – 将 JSON 文件在内存里以树的形式表示,通过 JsonNode 处理单个 Json 节点,类似于 XML 的 DOM 解析器(数模型 Json 文件在内存里以树形式表示 ObjectMapper 构建 JsonNode 节点树 – 最灵活)
- databind 模块 – ObjectMapper 读/写 JSON ,是序列化与反序列化 json 最方便的方式(本篇实战采用的方法)
JavaBean 与 json 字符串相互转换
对于 JavaBean,我们创建 Student 类(注意使用 jackson 的类必须有空参构造器):
public class Student {
private String name;
private int age;
private String sex;
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//空参构造器
public student(){}
// get、set方法
// toString方法
}
复制代码
在测试代码中,首先创建一个 ObjectMapper 对象,序列化和反序列化都需要它。然后根据 writeValueAsString()
这个函数就可以把 java 对象转成 json 字符串(序列化)。而 json 字符串通过 readValue()
就可以将 json 字符串转化为 java 对象(反序列化):
// 创建 ObjectMapper 对象
ObjectMapper mapper=new ObjectMapper();
// 序列化的实体类
student stubigsai=new student("bigsai",6,"man");
// writeValueAsString()方法进行序列化
String stubigsaistr=mapper.writeValueAsString(stubigsai);
System.out.println(stubigsaistr+"\n");
// 反序列化的json字符串
String stuxiaohongstr="{\"name\":\"xiaohong\",\"age\":8,\"sex\":\"woman\"}";
// readValue()方法进行反序列化
student stuxiaohong= mapper.readValue(stuxiaohongstr,student.class);
System.out.println(stuxiaohong.toString());
复制代码
Java 集合与 Json 字符串相互转换
除了 JavaBean 和 json 字符串转换外,java 集合和 json 字符串的转换也很简单,我们这里就只演示 java 的 Map 与 json 字符串相互转化了,其流程与上述的 JavaBean 和 json 互转有点类似。需要注意的是从 json 转换为 Map 对象(或其他泛型对象)的时候,由于 Java 的类型擦除,有些无法直接正确转换的类型需要我们手动用 new TypeReference
给出,实例代码如下:
//创建 ObjectMapper实体类
ObjectMapper mapper=new ObjectMapper();
//需要序列化的Map和List
Map<String,Object> map1=new HashMap<>();
map1.put("one","1");
map1.put("two",2);
map1.put("string",new String[]{"w", "Q"});
//序列化结果
String map1str=mapper.writeValueAsString(map1); // {"string":["w","Q"],"two":"2","one":"1"}
//方式一反序列化结果
Map<String,Object> m1=mapper.readValue(map1str,Map.class); // {string:[w, Q],two:2,one:1}
//方式二TypeReference指定反序列化类型(适合泛型和复杂类型)
Map<String,Object> m2=mapper.readValue(map1str, new TypeReference<Map<String, Object>>(){}); // {string:[w, Q],two:2,one:1}
复制代码
看完实例,你是不是发现:哇,原来三者有很相似的之处啊。是的,一个优秀的开源框架不光要考虑其功能、性能,其易用性、对用户友好程度、官方文档等也是非常重要的指标!当然,这里仅仅是 jackson 功能的九牛一毛,更详细深入的学习需要到 jackson官方文档 去查阅。
Gson vs FastJson vs Jackson
现在三种 java 的序列化/反序列化 json 框架都已经介绍啦。三者都是非常优秀的框架,这是毋庸置疑的,但是一对比,它就有一些伤害。
名称 | Gson | fastjson | Jackson |
---|---|---|---|
社区生态 | 较好 | 一般 | 较好 |
易用性 | 易用 | 易用 | 一般 |
小文件速度 | 最快 | 较快 | 较慢 |
大文件速度 | 较慢 | 较快 | 较快 |
稳定性 | 稳定 | 一般 | 稳定 |
安装包大小 | 非常小 | 较小 | 一般 |
综上所述:
- Gson:轻便简洁,适合中小 json 文件的解析,在大 json 文件上效率略差,其功能比较全面,生态较好
- fastjson:速度较快,但经常爆出安全问题,生态一般,主要国内使用
- jackson:处理大文件速度快一些,且比 fastjson 稳定一些,生态较好
对选取哪个 json 框架主要根据自己的需要,如果是测试或者比较简单个人使用,推荐 Gson 和 fastjson 上手比较容易;如果是需要上线,那么 fastjson 得慎用,可能某些数据会序列化失败。
在这里,本篇对 json 的介绍、json 语法使用以及 Gson、fastjson、jackson 的一些实战的介绍就此已经完毕了,json 很容易,但 json 也可能很复杂,这要取决你具体业务的使用和需求,希望在以后的日子里,你能够对 json 的理解和应用能够有更深一步的认知!
小试牛刀
上面讲了那么多,对于我们来说掌握 java 中操作 json 很重要,那么咱们动手进行一个转换小案例。这里咱们要求用 Gson、fastjson 将下面对象进行转化:
JavaBean 转 JSON 字符串
假设我们有个 Teacher 类有 name
和 age
两个属性。
public class Teacher {
private String name;
private int age;
//省略 get、set 以及构造函数
}
复制代码
首先我们创建一个对象
Teacher teachersai = new teacher("Q",26);
复制代码
Json 格式的字符串:
{"name":"w", "age":19}
复制代码
如果是 Gson,我们是这样操作的:
Gson gson = new Gson();String teacherstr = gson.toJson(teachersai);System.out.println(teacherstr); // {"name":"Q","age":26}
复制代码
而如果是 fastjson,它又会是怎样的呢?
String teacherstr = JSON.toJSONString(teachersai);System.out.println(teacherstr); // {"name":"w","age":26}
复制代码
这样就完成 JavaBean 转成 json 字符串啦,是不是非常简单?!
因为 fastjson 将 JSON 中的很多方法写成静态 static,所以我们连 fastjson 对象都不需要创建。
JSON 字符串转 JavaBean
上面咱们轻轻松松的就可以将 JavaBean 转成 json 字符串,下面如果咱们有这么一个字符串需要转成 java 的 Teacher 对象,需要怎么做呢?
String teacherJ="{\"name\":\"one\",\"age\":19}"; //{"name":"one","age":19}
复制代码
那么创建完 Gson 对象只需一行代码就搞定:
Gson gson = new Gson();Teacher teacher = gson.fromJson(teacherJ,Teacher.class);
复制代码
同理,fastjson 也是一行代码搞定:
Teacher teacher = JSON.parseObject(teacherJ,Teacher.class);
复制代码
当然如果细心好学的你肯定会发现还有其他的写法,其余更多的用法肯定还需要自己去挖掘啦!
本文总结
轻松愉快的 json 介绍课程到这里就结束了,通过本节课程,你了解了 json 的概念,分清了 json 和 xml 的异同点,也知道了 json 在 web 领域和非关系数据库的那些应用。通过系列介绍、比较、结合实际场景相信你对 json 的认识肯定都是非常清晰的了。
紧接着文章又介绍了 json 的语法规则以及在 java 中操作 json 的实战操作,学习和比较了 Gson、fastjson、jackson 的应用以及它们之间的区别点,相信你也掌握了在 java 语言中 json 的实战能力,且能够根据自己需求灵活使用哪种框架,并深入的研究下去。
JSON 是 Web 技术进步与发展的产物,它是一种数据交换格式,它几乎是前后端程序员不可避免的技术点。
若本文对你有所帮助,点赞支持嗷~?
本文系转载 ?:juejin.cn/post/685457…