第四章 XML和JSON
1 XML
1.1 简介
可扩展标记语言(软件与软件交流时可用的语言)
特性:
- xml具有平台无关性,是一门独立的标记语言(不论是系统平台的不同还是语言平台的不同)
- xml具有自我描述性
1.2 学习XML的作用
- 网络数据传输(结构化传输)
- 数据存储
- 配置文件
1.3 XML的语法格式
1. XML文档的声明:<?xml version="1.0" encoding="UTF-8"?> (展现了自我描述性)
2. 标记(元素/标签/节点)
<names>
<name>张三</name> //名称不能以数组和标点符号开始,不能以xml开头
</names>
3. 一个XML文档中必须有且只有一个根标记
4. 标记可以嵌套,但是不可以交叉
5. 标记的名称可以重复
6. 在开始标记中可以有n个属性,同一个标签中属性名不能重复
7. 注释:注释不能写在文档声明前面
举例:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id = "1001">
<name>金苹果</name>
<info>讲述了苹果种植的故事</info>
</book>
<book id = "1002">
<name>银苹果</name>
<info>讲述了银苹果的故事</info>
</book>
</books>
1.4 如何解析XML
1.4.1 SAX解析
事件驱动机制
逐行读取XML文件解析,每当解析到一个标签的开始/结束/内容/属性的时候,触发事件,我们可以编写程序在这些事件发生的时候进行相应的处理
优点:
- 分析能够立即开始,而不是等待所有数据被处理
- 逐行加载,节省内存,有助于解析大于徐通内存的文档
- 有时不必解析整个文档,可以在某个条件得到满足的时候停止解析
缺点:
- 单向解析,无法定位文章层次,无法同时访问同意文档的不同部分数据(因为是逐行解析,解析当前行的时候上一行已经被释放了)
- 无法得知事件发生时元素的层次,需要程序员自己去维护节点的父/子关系
- 只读解析方式,无法修改XML文档的内容
1.4.2 DOM解析
分析该结构需要加载整个文档和内存中建立文档树模型,可以通过操作文档树来完成数据的获取,修改,删除等
优点:
- 文档在内存中加载,允许对数据和结构做出更改
- 访问是双向的,可以在任何时候在树中双向解析数据
缺点:
- 文档全部加载在内存中,消耗资源大
1.4.3 JDOM解析
目的是成为Java特定文档模型,简化了与XML的交互,并且比使用DOM实现更快,是第一个Java特定模型,JDOM一直得到大力推广与促进
优点:
- 使用具体类而不是接口,简化了DOM的API
- 大量使用的Java的集合类,方便了Java开发人员
缺点:
- 没有较好的灵活性
- 性能不是那么优异
1.4.4 DOM4J
是JDOM的一种智能分支,解决了一些JDOM存在的问题,面向接口,因此更加灵活
示例:使用DOM4J解析本地文件:
public class Demo1 {
public static void main(String[] args) throws IOException, DocumentException {
//1. 创建指向XML文件的输入流
FileInputStream fis = new FileInputStream("d://haha//xmlFiles//xmlDemo.xml");
//2. 创建一个XML读取工具对象
SAXReader reader = new SAXReader();
//3. 使用读取工具对象,读取XML文件的输入流,并得到文档对象
Document doc = reader.read(fis);
//4. 通过文档对象,获取XML文档中的根元素对象
Element root = doc.getRootElement();
//5. 打印根元素对象的名称
System.out.println(root.getName());
//6. 逐级获取文件的内容
List<Element> es = root.elements();
for (int i = 0; i < es.size(); i++) {
Element book = es.get(i);
System.out.println(book.attributeValue("id"));
System.out.println(book.elementText("name"));
System.out.println(book.elementText("info"));
System.out.println("----------------------");
}
fis.close();
}
}
示例:解析网络文件的xml:
public class Demo2 {
public static void main(String[] args) throws IOException, DocumentException {
//接受用户输入手机号
Scanner in = new Scanner(System.in);
String phone = in.nextLine();
//1. 创建一个url对象
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
//获得基于上述url的输入流
InputStream is = conn.getInputStream();
//2. 创建xml读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象读取xml数据,并返回文档对象
Document doc = sr.read(is);
//4. 获取到根节点
Element root = doc.getRootElement();
//5. 解析内容
String code = root.elementText("resultcode");
if ("200".equals(code)){
Element result = root.element("result");
String province = result.elementText("province");
String city = result.elementText("city");
if (city.equals(province)){
System.out.println("号码归属地为"+city);
}else {
System.out.println("号码归属地为"+province+"省"+city+"市");
}
}else {
System.out.println("请输入正确的手机号码");
}
}
}
1.5 DOM4J-XPATH解析XML
路径表达式:
通过路径快速的查找一个或一组元素
路径表达式:
1. /:从根节点开始查找
2. //:从发起查找的节点位置 查找后面的节点
3. . :查找当前节点
4. .. :查找父节点
5. @ :选择属性:属性的使用方式:
(1)[@attribute='value']
(2)[@attribute>'value']
(3)[@attribute<'value']
(4)[@attribute!='value']
e.g. //book[@id='1']//name (从当前节点查找book节点中属性id='1'的节点,得到它的name节点)
xpath常用方法:
1. Node selectSingleNode("path name");//通过路径表达式获得节点(Node是Document和Element的父接口)
2. List<Node> selectNodes("path name");//通过路径表达式获得节点列表,同一路径下的所有节点
示例:从文件中利用xpath解析xml:
public class Demo3 {
public static void main(String[] args) throws IOException, DocumentException {
//1. 创建指向XML文件的输入流
FileInputStream fis = new FileInputStream("d://haha//xmlFiles//xmlDemo.xml");
//2. 创建一个XML读取工具对象
SAXReader reader = new SAXReader();
//3. 使用读取工具对象,读取XML文件的输入流,并得到文档对象
Document doc = reader.read(fis);
//4. 通过文档对象+xpath,查找所有的name节点
List<Node> names = doc.selectNodes("//name");
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i).getName());
System.out.println(names.get(i).getText());
}
}
}
示例:从网络文件利用xpath解析xml
public class Demo4 {
public static void main(String[] args) throws IOException, DocumentException {
Scanner in = new Scanner(System.in);
String phone = in.nextLine();
//1. 创建一个url对象
URL url = new URL("http://apis.juhe.cn/mobile/get?phone="+phone+"&dtype=xml&key=9f3923e8f87f1ea50ed4ec8c39cc9253");
URLConnection conn = url.openConnection();
//获得基于上述url的输入流
InputStream is = conn.getInputStream();
//2. 创建xml读取对象
SAXReader sr = new SAXReader();
//3. 通过读取对象读取xml数据,并返回文档对象
Document doc = sr.read(is);
//直接获得运营商(直接找到公司节点)
Node node = doc.selectSingleNode("//company");
System.out.println(node.getText());
}
}
1.6 生成XML
手动添加节点输出xml
public class Demo5 {
public static void main(String[] args) throws IOException {
//1. 通过文档帮助器,创建一个文档对象
Document doc = DocumentHelper.createDocument();
//2. 给文档添加第一个根节点
Element books = doc.addElement("books");
//3. 通过根节点,丰富子节点
for (int i = 0; i < 10; i++) {
Element book = books.addElement("book");
book.addAttribute("id",i+"");
Element name = book.addElement("name");
Element info = book.addElement("info");
name.setText("清明上河图"+i);
info.setText("描绘了宋代清明节前后的人文景观");
}
//4. 创建一个输出流
FileOutputStream fos = new FileOutputStream("d://haha//xmlFiles//output.xml");
//5. 将输出流包裹成xml输出流
XMLWriter xr = new XMLWriter(fos);
//6. 输出
xr.write(doc);
System.out.println("输出完毕");
}
}
利用xstream直接得到对象的xml字符串
public class Demo6 {
public static void main(String[] args) {
Person p = new Person("张三",18);
//1. 创建XStream都西昂
XStream xs = new XStream();
//2. 修改某个类型生成的节点(默认为包名.类名)
xs.alias("Person", Person.class);
//3. 传入对象,开始生成
String xml = xs.toXML(p);
System.out.println(xml);
}
static class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
}
如果没有更改别名:
如果更改了别名:
2. JSON
简介:
JavaScript Object Notation JS对象简谱,是一种轻量级的数据交换格式
{
"name" : "金苹果",
"info" : "种苹果"
}
1. 一个对象由一个大括号表示,括号中描述对象的属性,通过键值对来描述对象的属性(大括号中包含的是一个个的键值对)
2. 格式:键与值之间用冒号链接,键和值用引号标注,键值对的值可以实JS中的任意类型的数据
数组格式:
[元素1,元素2 ...]
案例:
{
"name":"格兰特船长的儿女",
"info":"讲述了格兰特船长的相关故事"
"author":{
"name":"儒勒.凡尔纳"
"birth":"1802"
}
"related":["海底两万里","地心游记","八十天环游地球"]
}//对象中可以嵌套对象,值中可以放数组,数组中可以继续嵌套对象
2.1 解析JSON格式
GSON工具
//将对象转化为JSON字符串
public class Demo1 {
public static void main(String[] args) {
//1. 创建GSON对象
Gson gson = new Gson();
//2. 创建book对象
Book book = new Book("1001","The bad apple","about the hard work of planting apple");
//3. 转化
String s = gson.toJson(book);
System.out.println(s);
//{"id":"1001","name":"The bad apple","info":"about the hard work of planting apple"}
}
}
//将JSON字符串转化为对应的对象
public class Demo2 {
public static void main(String[] args) {
//1. 创建GSON对象
Gson gson = new Gson();
//2. 转换
String s = "{\"id\":\"1001\",\"name\":\"The bad apple\",\"info\":\"about the hard work of planting apple\"}";
Book b = gson.fromJson(s, Book.class); //需要传入要转化的对象
System.out.println(b.getId());
System.out.println(b.getName());
System.out.println(b.getInfo());//可以通过对象的get方法获得对象内部的内容
//如果传入的类是HashMap,则会返回HashMap类型的对象
HashMap map = gson.fromJson(s,HashMap.class);
System.out.println(map.get("name"));
//如果传入的JSON字符串包括了数组格式:解析出来就会变成List类型:
// {"id":"1001","name":"The bad apple","page":["page1","page2","page3"]}
String s1 = "{\"id\":\"1001\",\"name\":\"The bad apple\",\"page\":[\"page1\",\"page2\",\"page3\"]}";
HashMap data = gson.fromJson(s1,HashMap.class);
System.out.println(data.get("page"));
System.out.println(data.get("page").getClass());//发现page键对应的值是一个List
//[page1, page2, page3]
//class java.util.ArrayList
List list = (List) data.get("page");
System.out.println(list.get(1));//还可以取出list中的内容
}
}
FastJSON工具
//将Book对象转化为JSON字符串
Book book = new Book("1001","海底两万里","讲述了在海底的潜艇");
String s = JSON.toJSONString(book);
System.out.println(s);
//将JSON字符串转化为Book对象
Book book = JSON.parseObject("{\"id\":\"1001\",\"info\":\"讲述了在海底的潜艇\",\"name\":\"海底两万里\"}", Book.class);
System.out.println(book.getInfo());
//将JSON数组转化成List
String array = "[\"hello\",\"world\",\"this\",\"is\",\"clark\"]";
List<String> list = JSON.parseArray(array,String.class);//传入的类型为List中的泛型类型
System.out.println(list.get(0));