JSON是一种规范,你知道吗?
在本文中,通过规范来剖析JSON,本文的重心将更多放在介绍JSON为何是一种规范。同时也帮助大家更好的学习和理解JSON在开发中作用。
在了解JSON是什么之前,我们先来了解一下Web开发技术栈。
Web开发技术栈
在我的理解中,所谓的技术栈就是很多门技术一起搭配使用,共同完成一个项目。而这些技术分别服务于不同的技术范围,这些技术组合起来的有机体称为技术栈。
注意:这里的栈不是数据结构中的栈,而是数量词“堆”的引申。所以技术栈可以理解为技术堆,即一堆技术。
Web开发技术栈有很多很多,在这里,使用如下技术栈为例:
oracle/mysql + java + html + css + js
其中:oracle/mysql负责数据的存储,java负责数据的处理,html+css+js负责数据的展示。
学习过面向对象的小伙伴都应该知道,在面向对象中数据的载体是对象,上述技术栈中的java和js都是面向对象编程语言,java和js中都有对象,但是需要注意的是java中的对象表示形式和js中的对象表示形式是不一样,而且java和js在这条技术栈上要进行数据交换,那么就引申出了一个问题:java中的对象不能直接传输给js解析,同样的,js的对象不能直接传输给java解析。但是在实战开发中,需要java和js搭配使用,所以此时迫切需要一种技术(规范)用来解决不同语言之间进行数据交换的问题,这种技术就是数据传输格式。
什么是数据传输格式?
在我的认识内,数据传输格式可以理解为一种规范,是不同编程语言之间进行信息交换的一种规范,既然是一种规范,那么如果我们了解了什么是规范,那么我们不就弄懂了数据传输格式了吗?哈哈哈哈。这是我的一厢情愿,那么接下来聊聊我认识中的规范。
什么是规范?
规范可以借用java语言中的接口来理解,java中的接口的作用也是定义规范,使用过java的程序员都知道java中的类只要实现了接口,就一定拥有接口中定义的规范,或者说是能力。我更倾向于通过能力来理解接口,但是没有规范来理解接口高大上,但是在我看来,越是高深的东西,越是让人琢磨不透,即便那是一个很浅显的问题。为了能够和大家一起探讨什么是规范,容我使用一个需求来给大伙阐述我对规范的理解。
需求:实现一台打印机,这台打印机可以使用纸张来打印人们需要的数据资料。
需求分析:这个需求中存在三个待描述的事物:打印机、纸张和墨盒,所以对这个需求的分析我将分为两步走:事物的归类和事物之间的关系。
[1] 事物的归类:
学习过java的都知道,在Java中能用于描述的可以划分为三大类:具体类、抽象类和接口。我是对这三者的理解是,接口用于定义规范、抽象类用于描述事物、具体类同于实现细节。所以我将打印机归类为具体类,而将纸张和墨盒归类为接口,因为纸张的尺寸有很多种,如A4、A3、B5等等,墨盒也分为黑墨盒和彩色墨盒。所以把纸张和墨盒定义为接口是一个不错的选择。
[2] 事物之间的关系:
现实生活中的打印机具备打印功能,使用过打印机的人都知道,在这就不过多的讨论了,但是我们常常会忽略了打印机应该还具备换纸张和换墨盒的功能,虽然是我们手动的给打印机换纸张和墨盒,但是其实本质上来讲,这应该是属于打印机的功能,只不过可能是智能化水平限制了打印机的这个功能,但是5G时代的到来,相信这两个功能以后能由打印机自己来完成,毕竟5G时代的热词就是万物互联嘛,好了好了,废话了一大堆,回归正题,经过上面的分析,在我看来,打印机、纸张、墨盒三者之间的关系应该是打印机包含纸张和墨盒。
代码实现:
纸张接口:
/**
* 纸张接口
*/
public interface Paper {
/**
* @return 纸张的大小
*/
public String getSize();
}
纸张实现类:
public class A4Paper implements Paper {
@Override
public String getSize() {
return "A4";
}
}
public class B5Paper implements Paper {
@Override
public String getSize() {
return "B5";
}
}
墨盒接口:
public interface Ink {
/**
* @return 墨盒的颜色
*/
public String getColor();
}
墨盒实现类:
public class BlackInk implements Ink{
@Override
public String getColor() {
return "黑白";
}
}
public class ColorfulInk implements Ink {
@Override
public String getColor() {
return "彩色";
}
}
打印机:
public class Printer {
private Paper paper; // 打印机装载的纸张
private Ink ink; // 打印机装载的墨盒
public Paper getPaper() {
return paper;
}
public void setPaper(Paper paper) {
this.paper = paper;
}
public Ink getInk() {
return ink;
}
public void setInk(Ink ink) {
this.ink = ink;
}
public Printer() {
super();
}
public Printer(Paper paper, Ink ink) {
super();
this.paper = paper;
this.ink = ink;
}
/**
* 使用打印机打印info信息
*/
public void print(String info) {
String paperSize = paper.getSize(); // 获取打印机使用的纸张大小
String inkColor = ink.getColor(); // 获取打印机使用的墨盒颜色
System.out.println("打印机使用" + paperSize + "纸张和" + inkColor + "墨盒打印了:" + info);
}
}
测试类:
public class TestMain {
public static void main(String[] args) {
// 这里只选择A4Paper和ColorfulInk实现类来演示
Paper A4Paper = new A4Paper();
Ink colorfulInk = new ColorfulInk();
Printer printer = new Printer(A4Paper, colorfulInk);
printer.print("大兄弟,你最胖的!!!");
}
}
测试结果:
![7d74fda1d40a2f71cc0d7fec28656ac7.png](https://i-blog.csdnimg.cn/blog_migrate/367fc4a29e63c2df4dc624a01e7b0ea4.png)
这个例子中的墨盒接口和纸张接口以一种规范(能力)存在于打印机中,换句话说就是,打印机向外界定义了纸张尺寸和墨盒颜色,只要某一种纸张符合了打印机定义的纸张尺寸大小,就能成功的安装到打印机上,同理,知道某一种墨盒符合了打印定义的墨盒颜色,那么就能装载到打印机上。
希望看到这里的小伙伴或许对规范有一种感性的认识,即规范用于将两个基本没关系的两个东西给联系起来。
此时我们在回到编程语言的数据交换问题的探讨上,编程语言之间虽然都是你抄抄我,我抄抄你,但是谁都不肯跟谁低头,所以每一门的高级编程语言的思想都是基本一样的,但是就是对一样事物的表现形式不同,这就导致不同的编程语言之间的信息交换会出现障碍。
而清除这个障碍的就是数据传输格式,它给出了不同编程语言之间进行数据交换的同一格式,即定义了一套规范,如果某一个门编程语言要和另一门编程语言进行数据交换,那么就必须满足这套规范,要不就你别想轻轻松松的进行数据交换。
这里还是举java和js进行数据交换这个例子,目前前后端开发分得可谓是越来越细,这导致了前端开发和后端开发之间不再是一个人或者一个team来完成了,反之,是至少两个team来分工合作,我们都知道前端主要和用户打交道,而后端主要进行数据的处理,对于web开发来说,前端主流语言是javascript,而后端主流语言是java,如果对这两门语言有所了解的话,那么肯定知道js中的对象和java中的对象在表现形式上是有一些区别的,此时如果前端将用户输入的信息打包成一个js对象并把这个js对象传输到后端,后端主流语言java能识别到js对象吗?答案是不能!
针对这个问题,如果在js和java之间定义一种数据传输格式规范,js和java都遵循这种规范,那么js和java不就可以很愉快的进行信息交换了吗?这不好比只有打印机指定的纸张尺寸和墨盒颜色才能成功的安装到打印机上吗?所谓的指定不就是一种规范吗?此时在看看本文开头对数据传输格式的定义,如果你对数据传输格式能有了初步感性认识,那是最好的,如果还没有,可能是我对数据传输格式的认识和理解还不够深入,以至于没能给大家阐述清楚数据传输格式,还请多多包涵。
接下来是数据传输格式规范的应用例子,这可比言语更加能易懂,也好比看中文API时对某个方法的解释时,一头雾水,但是一看例子的时候,即恍然大悟。
XML数据传输格式
看到这个小标题时,学过HTML的小伙伴可能会有些许激动,因为XML和HTML和相像,其实它们俩没差太多,都是通过一对对尖括号组成的,不同的是:HTML主要用于数据的展示,XML主要用于数据的存储。在JSON还未问世之前,XML是不同语言之间进行数据传输的首选数据传输格式,因为其相对其他数据传输格式而言够简单明了,只需将数据通过一对对尖括号包裹起来即可。详见下一part。
XML存储数据的格式
需求:将一个人的姓名(张三)、年龄(18)、性别(男)信息使用XML进行存储。
<person>
<name>张三</name>
<age>18</age>
<sex>男</sex>
</person>
person标签用于指明标签内的每一个标签都是对person类对象的一个描述,如name标签、age标签、sex标签分别是姓名、年龄、性别。xml文件在java中可以当做配置文件进行解析,其实Eclipse编辑器对每一个工程配置的文件,也是xml文档,其中每一对标签携带的信息都是对工程的描述。
还是举js和java间的数据传输例子,如果前端需要将一个人的信息(js对象)装换成一个XML文件,然后将这个XML文件发送到后端,后端接收到这个xml文件之后,对齐进行解析,进而获取一个的信息(java对象)。这不就解决了js和java之间进行数据交换时,对象类型不能直接装换为问题了吗?
图示如下:
![a70e704dd5477e964aca6fa53d3d846a.png](https://i-blog.csdnimg.cn/blog_migrate/b697cd0865019bf1f584c444fa288efe.png)
XML的优点
XML在存储和表现数据时,最大优点就是简单(相对而言)、清晰、明了、容易解析(解析效率高)、高可移植性、高可扩展性。
XML的缺点
XML在存储数据时都要通过一对尖括号包裹,但我们存储的信息比较少时,标签的嵌套是可以接受的,而且现在还有emmet插件,那都不是什么大事,但是在开发中需要传输的数据,一般都是比较复杂,过于复杂的数据会出现标签嵌套过度,导致xml代码可维护性低,这好比if elseif else 语句的多层嵌套,那可谓是看得眼花缭乱,其次是代码执行效率低,所以在java中如果if语句判断超过三层,那么就需要review一下代码和重新阅读下需求了。
代码冗余例子:
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>
two of our famous Belgian Waffles with plenty of real maple syrup
</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>$7.95</price>
<description>
light Belgian waffles covered with strawberries and whipped cream
</description>
<calories>900</calories>
</food>
<food>
<name>Berry-Berry Belgian Waffles</name>
<price>$8.95</price>
<description>
light Belgian waffles covered with an assortment of fresh berries and whipped cream
</description>
<calories>900</calories>
</food>
<food>
<name>French Toast</name>
<price>$4.50</price>
<description>
thick slices made from our homemade sourdough bread
</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>
two eggs, bacon or sausage, toast, and our ever-popular hash browns
</description>
<calories>950</calories>
</food>
</breakfast_menu>
餐厅每推出一种新菜品,那么都必须编写一摸一样的标签,仅仅只是标签包裹的内容不同。
JSON数据传输格式
JSON全称是JavaScript Object Notation,是ECMAScript的一个子集,相对XML而言,JSON是一种轻量级数据交换格式。
JSON的优点
- 数据格式简单。
- 上手容易,高效。
- 易于读写。
- 占用带宽低。
- 易于解析。
- 大多数语言都非常好的支持JSON,如Java中的GSON、fastJSON等等。
- XML能做的,JSON都能做。
JSON字符串的格式
JSON只能构建对象和数组两种数据结构。
对象的JSON字符串形式:
编写的流程图:
![48337fc17eb27d5909463f38a30d1ef4.png](https://i-blog.csdnimg.cn/blog_migrate/4f30980bc50728643405125cadae8985.png)
编写语法:
"{ "属性名1" : 属性值1, "属性名2" : 属性值2, ...}"
数组的JSON字符串形式:
编写的流程图:
![308301bd0b321b60fa744f63465dbc2a.png](https://i-blog.csdnimg.cn/blog_migrate/fd51494b53d42da21f2e6b14111bbd84.png)
编写语法:
"[ 元素值1, 元素值2, ... ]"
JSON字符串的约束
表示对象时:
- 属性名必须使用双引号包裹。
- 属性名和属性值之间通过分号分割,并且还带有一个空格。
- 当出现字符串类型的属性名或者属性值时,需要对双引号进行转义。
- 属性值只能是number、string、boolean、array、object、null。
表示数组时:
- 当出现字符串类型的元素值时,需要对双引号进行转义。
- 属性值只能是number、string、boolean、array、object、null。
JS中JSON字符串的使用
因为JSON是ECMAScript的一个子集,所以js自带了名为JSON的JSON字符串库,js开发者直接使用即可,不需要导包。
两个重要的方法:
JSON.stringify(object/array) : string,将js对象转化为JSON字符串。
JSON.parse(string) : object/array,将JSON字符串转化为js对象。
代码实现:
![3740150128093b7277f3d20580efd1db.png](https://i-blog.csdnimg.cn/blog_migrate/66be3ae73adae4df20109ba1b36e2e4c.jpeg)
测试结果:
![61f13da95c75410bd2b4d1d70af5a66a.png](https://i-blog.csdnimg.cn/blog_migrate/7da3d2f76d5c0cd7fd438ef6e94f8d06.jpeg)
java中JSON字符串的使用
java中没有原生JSON字符串工具库,但是有很多优秀的开发者编写了java的JSON字符串工具库,如fastJSON、GJSON、JSONUtil。
fastJSON
在Eclipse中使用fastJSON工具库时,需要将fastJSON.jar包添加到JVM解释执行路径中。
注意:.jar包中包含的都是.class文件。
三个重要的方法:
JSON.toJSONString(Object/Array) : String,将java对象/数组转化为JSON字符串。
JSON.parseObject(String, Class) : Object,将JSON字符串转化为java对象。
JSON.parseArray(String, Class) : Array,将JSON字符串转化为java数组。
注意:java中的数组指的是List家族。
代码实现:
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.JSON;
public class TestMain {
public static void main(String[] args) {
// java对象 <---> JSON字符串
Person p = new Person("张三", 18, "男");
String pStr = JSON.toJSONString(p); // java对象 ---> JSON字符串
System.out.println(pStr);
Person p2 = JSON.parseObject(pStr, Person.class); // JSON字符串 ---> java对象
System.out.println(p2);
System.out.println("——————我是一个华丽的分隔符————————");
// java数组 <---> JSON字符串
ArrayList<Person> arr = new ArrayList<Person>();
arr.add(new Person("李四", 19, "男"));
arr.add(new Person("王五", 20, "男"));
String arrStr = JSON.toJSONString(arr); // java数组 ---> JSON字符串
System.out.println(arrStr);
List<Person> arr2 = JSON.parseArray(arrStr, Person.class); // JSON字符串 ---> java数组
System.out.println(arr2);
}
}
测试结果:
![136d05ced2965ab2e00881b11fa927c7.png](https://i-blog.csdnimg.cn/blog_migrate/112977c6b883cfd1b8697892a382ba4a.png)
GJSON
GJSON在使用之前和fastJSON一样,都需要将jar包加载到jvm解释执行路径中。
三个重要的方法:
new Gson() : Gson,创建GJSON对象。
Gson实例对象.toJson(Object/Array) : String,将java对象/数组转化为JSON字符串。
Gson实例对象.fromJson(Object/Array, Class) : Object/Array,将JSON字符串转化为java对象/数组。
注意:GJSON在使用之间需要创建Gson对象。
import java.util.LinkedList;
import com.google.gson.Gson;
public class TestMain {
public static void main(String[] args) {
// java对象 <---> JSON字符串
Person p = new Person("张三", 18, "男");
Gson gson = new Gson();
String pStr = gson.toJson(p); // java对象 ---> JSON字符串
System.out.println(pStr);
Person p2 = gson.fromJson(pStr, Person.class); // JSON字符串 ---> java对象
System.out.println(p2);
System.out.println("——————我是一个华丽的分隔符——————");
// java数组 <---> JSON字符串
LinkedList<Person> arr = new LinkedList<Person>();
arr.add(new Person("李四", 19, "男"));
arr.add(new Person("王五", 20, "男"));
String arrStr = gson.toJson(arr);
System.out.println(arrStr);
LinkedList<Person> newArr = gson.fromJson(arrStr, LinkedList.class);
System.out.println(newArr);
}
}
测试结果:
![b2c5785c5fcd1121a860b59ef8e0e82f.png](https://i-blog.csdnimg.cn/blog_migrate/2c4113c2552ecb4aa22be1875c73f836.png)
XML和JSON的对比:
XML和JSON比较图:
![4da725854e525574ae4927662ac3e485.png](https://i-blog.csdnimg.cn/blog_migrate/18e55dedc56d11cf75cd9eca91da029e.jpeg)
通过上面图,我们可以更加清楚的看出,XML和JSON字符串都是作为一个中间人的身份存在,其实就是以一种规范存在,只要js和java遵循了这种规范,那么js和java之间的对象就可以轻松的进行转换,即信息交换。
总结
总的来说,JSON字符串解决了当下语言之间进行数据交换的难题,相比XML而言,JSON是一种轻量级的数据传输格式。如果第一次接触数据传输格式,那么建议初学者了解xml即可,而将重心方法学习JSON上,目前主流的数据传输格式已经慢慢转为JSON,而且JSON对初学者非常友好,上手快。