XML解析

XML 一种通用的数据交换格式,平台无关性、语言无关性、系统无关性、给数据集成与交互带来了极大的便捷。

XML 在不同的语言环境,解析方式都是一样的,只是实现的语法不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8">
<bookstore>
<book id="1">
<name>冰与火之歌</name>
<author>乔治马丁</author>
<year>2014</year>
<price>89</price>
</book>
<book id="2">
<name>安徒生童话</name>
<year>2014</year>
<price>77</price>
<language>English</language>
</book>
</bookstore>

DOM Document Object Model


参考java基础语法中最后一个知识点

SAX解析 Simple APIs for XML


XML 简单应用程序接口,SAX 童工的访问模式是一种顺序模式,这是一种快速读写 XML 数据的方法,当 SAX分析器 对 XML 文档进行分析时,会触发一系列事件,并激活响应的事件处理函数,应用程序通过这些事件处理函数实现对 XML 文档的访问,所以 SAX接口 也成为事件驱动接口。

优点:

  1. 不需要等待所有数据都被处理,分析就能立即开始
  2. 只在读取数据时检查数据,不需要保存在内存中
  3. 可以在某个条件得到满足时停止解析,不必解析整个文档

缺点:

  1. 需要应用程序自己负责 TAG 的处理逻辑(维护父子关系等),文档越复杂就越复杂
  2. 单向导航,无法定位文档层次,很难同时访问文档的不同部分的数据。不支持XPath。

1

xml 文件被 Sax解析器载入,由于 Sax解析器 是按照 xml 文件的顺序分析,

  1. 当读入<?xml...> 时,调用 startDocument()方法
  2. 读入 <bookstore> 的时候,由于它是 ElementNode 会调用 startElement(String uri, String localName, String qName, Attributes attributes) 方法,localName/qName 为节点的名称。attributes 是这个节点的属性。
  3. 找到<bookstore>,不需要它,接着往下找,从<book>节点开始,读入时,调用方法,可以通过 attributes.getValue(0)来得到。
  4. 接着往下找,图中标明[2]的地方,会调用 characters(char[] ch, int start, int length) 方法,不要以为是空白,Sax解析器会把它认为是一个 TextNode。
  5. 接着是 <name> 标签,会调用 startDocument() 此时需要记录下每个节点的标签名 TagName,然后再调用 characters() 方法遍历后面的 TestNode,当TagName 是我们需要的标签名时,将 characters() 中的数据读出即可。

注意:由于环境原因,调用startElement(String uri, String localName, String qName, Attributes attributes)方法时第二个参数有可能为空,可以使用第三个参数,因此在解析前,先调用一下看哪个参数能用。

SAX 不需要将数据存储到内存中,只是在读取数据时检查数据。

以下是代码和打印:


xml 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<name>冰与火之歌</name>
<author>乔治马丁</author>
<year>2014</year>
<price>89</price>
</book>
<book id="2">
<name>安徒生童话</name>
<year>2014</year>
<price>77</price>
<language>English</language>
</book>
</bookstore>

Book 类

1
2
3
4
5
6
7
8
public class Book {
private String id;
private String name;
private String author;
private String year;
private String price;
private String language;
}

Sax 工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Sax 解析 xml工具类 传入 xml 文件相对路径
* @author Ammar
*
*/
@SuppressWarnings("all")
public class Sax {
private static Sax instance = new Sax();
private Sax() {};
public static Sax getInstance() {
return instance;
}

public <T> List<T> parse(String url, Class<T> type) {
SAXParserFactory sf = SAXParserFactory.newInstance();
SAXParser sp = null;
try {
sp = sf.newSAXParser();
AMHander<T> hd = new AMHander(type);
sp.parse(url, hd);
return hd.getList();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

接下来是最终要的 Sax实现解析 xml文档的 实现类 继承 DefaultHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package cn.lizhaoloveit.day02._SAX;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class AMHander<T> extends DefaultHandler {
private List<T> list = new ArrayList<>(); // 存储对象
private Map<String, Object> map; // 存储类的属性和值
private final Set<String> keySet = new HashSet<String>(); // 存储类的属性 方便查找
private Class<T> type; // 用来 map -> class
private int Index; // 记录个数
private String context; // 记录标签与标签之间的文本

public List<T> getList() {
return list;
}

public AMHander (Class<T> type) {
this.type = type;
// class -> Map
try {
BeanInfo info = Introspector.getBeanInfo(type, Object.class);
PropertyDescriptor[] ps = info.getPropertyDescriptors();
for (PropertyDescriptor p : ps) {
keySet.add(p.getName());
}
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("SAX 文档解析开始");
}

/**
* 解析 xml 元素
* 1. localName/qName 节点名称
* 2. attributes 属性
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
// 如果发现了 解析类的标签
if (type.getSimpleName().equalsIgnoreCase(qName)) {
Index++;
// 创建 map 存储对象
map = new HashMap<String, Object>();
System.out.println("=============开始遍历某一个对象=============");
// 获取元素的属性名 以及属性值
for (int i = 0; i < attributes.getLength(); i++) {
if (keySet.contains(attributes.getQName(i))) {
System.out.println("属性名" + attributes.getQName(i) + "属性值" + attributes.getValue(i));
map.put(attributes.getQName(i), attributes.getValue(i));
}
}
} else {
System.out.println("字段名:" + qName);
}
}

/**
* 解析标签与标签之间的文本会调用
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
context = new String(ch, start, length);
if (!context.trim().equals("")) {
System.out.println("节点值是:" + context);
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if (type.getSimpleName().equalsIgnoreCase(qName)) {
// 结束了一本书的遍历
// map -> javabean
try {
BeanInfo info = Introspector.getBeanInfo(type, Object.class);
PropertyDescriptor[] ps = info.getPropertyDescriptors();
T instance = type.newInstance();
for (PropertyDescriptor p : ps) {
Object value = map.get(p.getName());
Method m = p.getWriteMethod();
m.invoke(instance, value);
}
list.add(instance);
} catch (IntrospectionException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("=====================结束遍历一本书的内容==============");
} else if (keySet.contains(qName)) {
// 找到了某个字段,直接赋值
map.put(qName, context);
System.out.println("赋值字段" + qName + "成功");
}
}

@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("SAX 文档解析结束");
}
}

  • startDocument() 方法只会

JDOM解析


特点:仅适用具体类,不适用接口。API 大量使用 Conllections 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class JDOMTest {
private static ArrayList<Book> booksList = new ArrayList<Book>();
/**
* @param args
*/
public static void main(String[] args) {
// 进行对books.xml文件的JDOM解析
// 准备工作
// 1.创建一个SAXBuilder的对象
SAXBuilder saxBuilder = new SAXBuilder();
InputStream in;
try {
// 2.创建一个输入流,将xml文件加载到输入流中
in = new FileInputStream("src/res/books.xml");
InputStreamReader isr = new InputStreamReader(in, "UTF-8");
// 3.通过saxBuilder的build方法,将输入流加载到saxBuilder中
Document document = saxBuilder.build(isr);
// 4.通过document对象获取xml文件的根节点
Element rootElement = document.getRootElement();
// 5.获取根节点下的子节点的List集合
List<Element> bookList = rootElement.getChildren();
// 继续进行解析
for (Element book : bookList) {
Book bookEntity = new Book();
System.out.println("======开始解析第" + (bookList.indexOf(book) + 1)
+ "书======");
// 解析book的属性集合
List<Attribute> attrList = book.getAttributes();
// //知道节点下属性名称时,获取节点值
// book.getAttributeValue("id");
// 遍历attrList(针对不清楚book节点下属性的名字及数量)
for (Attribute attr : attrList) {
// 获取属性名
String attrName = attr.getName();
// 获取属性值
String attrValue = attr.getValue();
System.out.println("属性名:" + attrName + "----属性值:"
+ attrValue);
if (attrName.equals("id")) {
bookEntity.setId(attrValue);
}
}
// 对book节点的子节点的节点名以及节点值的遍历
List<Element> bookChilds = book.getChildren();
for (Element child : bookChilds) {
System.out.println("节点名:" + child.getName() + "----节点值:"
+ child.getValue());
if (child.getName().equals("name")) {
bookEntity.setName(child.getValue());
}
else if (child.getName().equals("author")) {
bookEntity.setAuthor(child.getValue());
}
else if (child.getName().equals("year")) {
bookEntity.setYear(child.getValue());
}
else if (child.getName().equals("price")) {
bookEntity.setPrice(child.getValue());
}
else if (child.getName().equals("language")) {
bookEntity.setLanguage(child.getValue());
}
}
System.out.println("======结束解析第" + (bookList.indexOf(book) + 1)
+ "书======");
booksList.add(bookEntity);
bookEntity = null;
System.out.println(booksList.size());
System.out.println(booksList.get(0).getId());
System.out.println(booksList.get(0).getName());

}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

DOM4J解析


特点:

  1. JDOM 的一种智能分支,合并了许多超出基本 XML 文档表示的功能。
  2. 适用接口和抽象基本类方法
  3. 具有性能优异、灵活性好、功能强大和极端易用的特点。
  4. 开源
  5. 支持 XPath
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private ArrayList<Book> list = new ArrayList<Book>();

@Test
public void testDOM4J() throws Exception {
// 准备工作,
// 1. 创建 SAXReader 的对象 reader
SAXReader reader = new SAXReader();
File file = new File("resources/books.xml");
try {
// 通过 read 方法加载 books.xml 文件,获得document 对象
Document document = reader.read(file);
// 获取根节点
Element root = document.getRootElement();
// 通过 element 对象的 elementIterator方法回去迭代器
Iterator it = root.elementIterator();

while (it.hasNext()) {
System.out.println("===========开始遍历谋一本书============");
Element book = (Element) it.next();
// 获取 book 的属性名和属性值
List<Attribute> attrs = book.attributes();
for (Attribute attr : attrs) {
System.out.println("属性名:" + attr.getName() + "--------属性值:" + attr.getValue());
}
Iterator itt = book.elementIterator();
while (itt.hasNext()) {
Element bookChild = (Element) itt.next();
System.out.println("节点名:" + bookChild.getName() + "-----节点值:" + bookChild.getStringValue());

}
System.out.println("===============结束遍历一本书===============");
}
} catch (Exception e) {
e.printStackTrace();
}
}

XML DTD 约束

由于 xml 的标签由用户自己定义, 因此在开发的时候,每个人都根据需求自定义 xml 标签,导致 xml 难以维护,需要规范机制约束 xml

文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2019/07/03/XML%E8%A7%A3%E6%9E%90/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论