在现实的开发当中,我们会遇到多个系统与系统之间,项目与项目之间进行数据的交换。在这种情况下,我们要如何来实现呢?
A程序当中有一个天气对象,里面放上的是今天的天气信息。B程序需要在它的界面上显示今天的天气,但是它又没有手段自己去获取数据,那只能找A程序要。
那A程序如何把这个数据给到B程序呢?A程序可以通过对象的序列化手段,把它的天气对象转成二进制流,然后通过网络把这个流传递给B程序。B程序再通过对象的反序列化手段把接收到的二进制流转回为一个Java对象。这么做的前提条件是A和B都必须是Java程序。
那么,有没有更好的方式可以适应再各种情况下都能够进行数据传递呢?
有!我们来分析一下,在这种情况下,我们要求双方传递的是什么?是数据!而数据就要有数据类型,不同的编程语言的数据类型不一致。但是有一种数据类型可以装入所有的数据,且不引起歧义,且所有的编程语言都拥有,它就是*字符串*。所以,我们可以使用字符串来进行数据的传递。
比如A程序向B程序传递数据:“晴32度北风2级湿度百分之20”。那么B程序就需要解析这个数据。B如何解析呢?比如它如何获取温度呢?从第2个字符开始取吗?取到第3个字符吗?
人用肉眼看是没有问题的,但是程序不行,它只能根据我们给它的指令来。如果我们给的指令是从第2个字符开始,那么如果信息数据变成"多云25度...."或者"多云转晴28度....."呢?
另外网络传输还有一个很重要的要求,那就是传递的数据应该尽量的精简,以达到节约信道的作用。
那么我们需要什么样的协议格式才能满足刚才说的通用性和精简性呢?经过长期的设计和实践的晒选,我们现在在实际应用当中看到还有两种手段:XML 和 JSON。
XML
XML的历史
XML的全称:可扩展标记性语言(eXtensible Markup Language)。
最早XML的提出不是为了用来进行数据传递,而是用来准备替换HTML的。它的目的是允许程序员在开发页面的时候不用使用HTML当中那些固定的标记,而是可以自己扩展自己的符号。比如:<按钮>提交</按钮>。但是,这件事情最终以HTML自身升级,然后XML失败告终。
但是,XML在另一个领域找到它的发展,就是“数据表达”。XML的格式能够非常清晰的表明各种复杂的数据结构,并且由于可扩展性,它还可以允许自定义标记、属性、嵌套关系。所以,它当时称为了“数据表达格式”的首选,并且一直用到现在。主要用于:数据传递(现在受到JSON的威胁)和配置文件。
合法的XML
XML当中最主要掌握它的四种语法。
1、元素
<元素名></元素名> --- 开始和结束标记
<元素名/> --- 单标记
2、属性
<元素名 属性名="属性值" 属性名2="属性值2">
3、文本
<元素名> 文本内容 </元素名>
4、注释
<!-- 注释内容 -->
注意:
XML的语法要求比HTML更严格,它要求:
1、有开始标记,必须要有结束标记;除非写成单标记;
2、标记与标记必须要有正确的嵌套 --- 如果一个标记包含了另一个标记的开始,就必须包含它的结束;
3、能且只能有一个根标记。
满足上面所有语法要求的XML,被称为“合法的XML”。合法是指合语法。
有效的XML
光是合法的XML在实际使用的时候还不够,因为同样的信息可能会有不同的XML写法,但都是合法的。
<成都>
<天气>晴</天气>
<温度>32</温度>
<风向>北</风向>
<湿度>20%</湿度>
</成都>
也可以写成:
<成都 天气="晴" 温度="32" 风向="北" 湿度="20%"/>
这个时候就需要通讯双方(构造XML数据的人 和 解读XML数据的人 )要统一规范了。而这个规范就是所谓的“文档类型定义”。它规范了这篇XML能够书写哪些标记?标记与标记的嵌套关系?标记当中有没有属性?属性叫什么名字?能够赋什么值?
要满足“文档类型定义”的XML,才能够在实际应用中起到有效的作用---让双方完成正确的数据沟通。
结论:
1、合法的XML不一定是有效的;
2、有效的XML一定是合法的。
如何定制XML的“文档类型定义呢”?在目前,有两种方式:DTD 和 Schema。这辆中方式:DTD简单/Schema繁琐;DTD比较粗/Schema细致。
DTD
DTD -- Document Type Defintion -- 文档类型定义
它的语法是这样的:
<!ELEMENT 元素 子元素> 子元素可以用 ? + * 等描述子元素出现的次数;
也可以用 ( ) 对子元素进行分组,然后元素与元素之间用|分隔表示可选,或,分隔表示顺序。
<!ELEMENT 元素 #PCDATA> <!ELEMENT 元素 EMPTY>
<!ELEMENT 成都 (天气,温度,风向,湿度)>
<!ELEMENT 天气 #PCDATA>
<!ELEMENT 温度 #PCDATA>
<!ELEMENT 风向 #PCDATA>
<!ELEMENT 湿度 #PCDATA>
示例:
<书籍>
<著作>
<书名>
<正书名>水浒传</正书名>
<副书名>105个男人和3个女人的故事</副书名>
</书名>
<作者>
<国籍>明</国籍>
<姓名>施耐庵</姓名>
</作者>
<出版社>中国铁道出版社</出版社>
<出版年月>1989.10.1</出版年月>
<页数>850</页数>
<价格>5.4</价格>
</著作>
<著作>
......
</著作>
</书籍>
<!ELEMENT 成都 EMPTY>
<!ATTLIST 成都
天气 (晴|阴|雨|) #REQUIRED
温度 CDATA #REQUIRED
风向 CDATA #IMPLED
湿度 CDATA #REQUIRED>
Schema
这是一种替代DTD的新的规范,它本身的语法也是XML的语法,而且定义的内容更加的细致,包括数据类型这些,它都有。
XML的解析
XML根据DTD/Schema书写完毕以后,解读方就要进行读取,获取当中的数据了。
XML的解析方式分两种:
1、DOM解析
DOM解析的思路是把整篇XML文件读取到内存当中,形成一颗完整的文档树。 它会把整篇文档中的所有信息按造对象的方式封装成一个个的节点Node对象,然后再用关联关系让上层和下层节点发生关联。 我们可以通过节点与节点之间的关系,在这颗文档树上进行来回的遍历,从而操作到每一个节点。
2、SAX解析
SAX解析的思路是比如我们要操作"作者"这个元素,那么SAX解析就会把我们的XML内容从上往下读取一遍,一旦遇到“作者”元素那么就会以“事件触发”的方式调用我们的执行代码。
DOM和SAX的区别:
1、DOM解析更适合与小型的XML,SAX适合于大型的XML;
2、DOM解析再形成文档树之后就可以根据节点关系在这颗树上反复来回操作。SAX解析呢,只能够从上往下读取一次,不能回头。
3、大部分的XML解析工具都是两者都会取它们的优势,合并使用。
JSON
JSON -- JavaScript Object Notation -- JS对象简谱。 它是一种轻量级的数据传输格式,现在几乎已经成为在数据传递这个使用场景当中的不二选择。虽然,它的名字当中有JavaScript,但请大家记住:它是一种字符串格式,与任何编程语言无关。任何编程语言都可以用它。
它的名字中为什么带有JS呢?这是因为它的语法起源于JavaScript对象的字面量表示方式。
JSON采用了JS中两种基本的字面量表现形式来构建它的数据:
1、数组形式 “[元素1,元素2,元素3]”
2、对象形式 {"属性名1"=属性值1,"属性名2"=属性2,......}
3、更复杂的数据结构就用它们之间的各种嵌套表示。
比如之前天气的例子: {"城市":"成都","温度":32,"天气":"晴"}
我们可以很明显的感觉到这种JSON格式的数据既可以表现各种丰富的数据结构,同时也比XML更加简洁。 大家只需要掌握住"[]"、"{}"、“,”、“属性名:属性值”
正是由于JSON满足了字符串形式,可以有丰富的数据结构,简洁性,所以现在JSON在做数据传递这个方向的时候是“不二选择”!
那么作为Java程序开发的我们需要掌握什么呢?我们需要掌握的是可以把我们程序中Java对象转成JSON格式的字符串,同时也可以把人家传递过来的JSON格式的字符串转成Java对象。我们把这种操作叫做“JSON的序列化”和“JSON的反序列化”。
当然,这些功能是不需要我们自己去写代码实现的,先人早就写好了,我们只需要调用先人的代码就可以了。
示例阿里的FastJson:
//1、java对象转JSON字符串
Student stu = new Student("zhang3",23,true, LocalDate.of(2008,5,12));
String stuJson = JSON.toJSONString(stu);
System.out.println(stuJson);
//2、JSON字符串转java对象
Student s1 = JSON.parseObject(stuJson,Student.class);
System.out.println(s1.getName());
//3、List对象转JSON字符串 --- [{},{},{}.....]
List<Student> stuLst = new ArrayList<>();
stuLst.add(new Student("zhang3",23,true, LocalDate.of(2008,5,12)));
stuLst.add(new Student("li4",21,false, LocalDate.of(2006,5,12)));
stuLst.add(new Student("wang5",25,false, LocalDate.of(2010,5,12)));
String lstJson = JSON.toJSONString(stuLst);
System.out.println(lstJson);
//4、JSON字符串转回List
List<Student> studentList = JSON.parseArray(lstJson,Student.class);
for(Student stu : studentList){
System.out.println(stu.getName() + "====" + stu.getAge());
}
//5、Map对象转换JSON字符串 --- { 键:{} ,键:{},..... }
Map<String, Student> map = new HashMap<>();
map.put("9527",new Student("zhang3",23,true, LocalDate.of(2008,5,12)));
map.put("9528",new Student("li4",21,false, LocalDate.of(2006,5,12)));
map.put("9529",new Student("wang5",25,false, LocalDate.of(2010,5,12)));
String mapJson = JSON.toJSONString(map,true);
System.out.println(mapJson);
//6、JSON字符串转换回Map对象
Map<String,Student> studentMap =
JSON.parseObject(mapJson,new TypeReference<Map<String,Student>>(){});
System.out.println(studentMap.get("9527").getName());