Java常用XML解析方式

解析XML

今天下午来总结下,Java抑或android常用的XML解析方式吧。过几天也该好好准备考试了。
Java中常用的XML解析方式有以下四种:

  • DOM:DOM解析XML文件时,需要将整个XML文件加载到内存,并以树形数据结构进行表示,接下来便是进行处理或者遍历。
  • JDOM:JDOM与DOM非常类似,它是处理XML的纯JAVA API,API大量使用了Collections类,且JDOM仅使用具体类而不使用接口。
  • SAX:基于事件驱动,不需要将整个XML文件加载到内存,用户只需要获取自己需要的标签即可。
  • DOM4J:dom4j是目前在xml解析方面是最优秀的(Hibernate、Sun的JAXM也都使用dom4j来解析XML),它合并了许多超出基本 XML 文档表示的功能,包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理
  • XmlPullParser(android):

因为,这些代码都是我测试过的,所以就不贴执行结果了,大家可以对比不同解析方式

首先,我们定义一个统一的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package me.wenyue.canglang.androidxmltest.parserDAO;

import java.io.InputStream;
import java.util.List;

import me.wenyue.canglang.androidxmltest.entity.demo;

/**
* Created by canglangwenyue on 15-6-26.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/
public interface XMLParser {
List<demo> parserXMl(InputStream in);
}

然后,我们需要一个待解析的XML文件。

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
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="0">
<name>Alexia</name>
<age>23</age>
<sex>Female</sex>
<salary>111</salary>
</user>
<user id="1">
<name>Edward</name>
<age>24</age>
<sex>Male</sex>
<salary>222</salary>
</user>
<user id="2">
<name>wjm</name>
<age>23</age>
<sex>Female</sex>
<salary>333</salary>
</user>
<user id="3">
<name>wh</name>
<age>24</age>
<sex>Male</sex>
<salary>444</salary>
</user>
</users>

DOM方式

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
132
133
134
135
package me.wenyue.canglang.androidxmltest.parserDAOImpl;

import android.util.Log;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import me.wenyue.canglang.androidxmltest.entity.demo;
import me.wenyue.canglang.androidxmltest.parserDAO.XMLParser;
import me.wenyue.canglang.androidxmltest.util.Config;


/**
* Created by canglangwenyue on 15-6-25.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class ParserXMLWithDOM implements XMLParser {
private final String TAG = "ParserXMLWithDOM";

@Override
public List<demo> parserXMl(InputStream in) {
List<demo> list = new ArrayList<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(in);
document.getDocumentElement().normalize();
Log.i(TAG + "ROOT", document.getDocumentElement().getNodeName());
Element root = document.getDocumentElement();
//拿到root节点的所有子结点
NodeList items = root.getElementsByTagName("user");
Log.i(TAG, "------------------------------");
for (int i = 0; i < items.getLength(); i++) {
Node node = items.item(i);
//拿到user的id属性
// Log.i(TAG, node.getNodeName() + node.getAttributes().item(0).getNodeValue());

if (node.getNodeType() == Element.ELEMENT_NODE) {
Element element = (Element) node;
Log.i(TAG, "user id=" + element.getAttribute("id"));
Log.i(TAG, "user name=" + element.getElementsByTagName("name").item(0).getTextContent());
Log.i(TAG, "user age=" + element.getElementsByTagName("age").item(0).getTextContent());
Log.i(TAG, "user sex=" + element.getElementsByTagName("sex").item(0).getTextContent());

//写入List
demo d = new demo();
d.setId(element.getAttribute("id"));
d.setAge(element.getElementsByTagName("age").item(0).getTextContent());
d.setName(element.getElementsByTagName("name").item(0).getTextContent());
d.setSex(element.getElementsByTagName("sex").item(0).getTextContent());
list.add(d);
}
}
} catch (
Exception e
)

{
e.printStackTrace();
}

return list;
}

public static void parserXMLWithRecursion(String filePath) {
File file = new File(filePath);
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(file);
System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
if (doc.hasChildNodes()){
printNode(doc.getChildNodes());
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

private static void printNode(NodeList childNodes) {
for (int count=0;count<childNodes.getLength();count++){
Node tempNode = childNodes.item(count);

// make sure it's element node.
if (tempNode.getNodeType()==Element.ELEMENT_NODE){
// get node name and value
System.out.println("\nNode Name =" + tempNode.getNodeName() + " [OPEN]");
System.out.println("Node Value =" + tempNode.getTextContent());
if (tempNode.hasAttributes()){
// get attributes names and values
NamedNodeMap nodeMap = tempNode.getAttributes();

for (int i = 0; i < nodeMap.getLength(); i++) {

Node node = nodeMap.item(i);
System.out.println("attr name : " + node.getNodeName());
System.out.println("attr value : " + node.getNodeValue());

}
}
if (tempNode.hasChildNodes()){
// loop again if has child nodes
printNode(tempNode.getChildNodes());
}
System.out.println("Node Name =" + tempNode.getNodeName() + " [CLOSE]");
}
}
}

public static void main(String[] args){
parserXMLWithRecursion(Config.getPath());
}


}

JDOM方式

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
package me.wenyue.canglang.androidxmltest.parserDAOImpl;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import me.wenyue.canglang.androidxmltest.entity.demo;
import me.wenyue.canglang.androidxmltest.parserDAO.XMLParser;
import me.wenyue.canglang.androidxmltest.util.Config;


/**
* Created by canglangwenyue on 15-6-26.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class ParserXMLWithJDOM
// implements XMLParser
{

// @Override
public static List<demo> parserXMl(InputStream in) {
List<demo> userList = new ArrayList<>();
SAXBuilder builder = new SAXBuilder();

try {
Document doc = builder.build(in);
Element rootNode = doc.getRootElement();

List list = rootNode.getChildren("user");

for (int i = 0; i < list.size(); i++) {
Element node = (Element) list.get(i);
//获取标签的value和属性
// System.out.println(node.getChildText("age")+node.getAttribute("id"));
demo de = new demo();
de.setAge(node.getChildText("age"));
//...其它省略
userList.add(de);
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return userList;
}

public static void main(String[] args) {
try {
List<demo> lists = parserXMl(new FileInputStream(Config.getPath()));
for (demo de:lists){
System.out.println(de.getAge());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

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
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
package me.wenyue.canglang.androidxmltest.parserDAOImpl;

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

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import me.wenyue.canglang.androidxmltest.entity.demo;
import me.wenyue.canglang.androidxmltest.parserDAO.XMLParser;


/**
* Created by canglangwenyue on 15-6-26.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class ParserXMLWithSAX
implements XMLParser {

@Override
public List<demo> parserXMl(InputStream in) {
SAXParserFactory factory = SAXParserFactory.newInstance();
final List<demo> list = new ArrayList<>();
try {
SAXParser parser = factory.newSAXParser();

DefaultHandler handler = new DefaultHandler() {

boolean name = false;
// boolean age = false;
// boolean sex = false;
// boolean salary = false;

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//如果需要解析标签的属性的话,便在这里进行
for (int i = 0; i < attributes.getLength(); i++) {
System.out.println(attributes.getLocalName(i) + ":" + attributes.getValue(i));
}
// System.out.println("Start Element :" + qName);
if (qName.equalsIgnoreCase("NAME")) {
name = true;
}

// if (qName.equalsIgnoreCase("AGE")) {
// age = true;
// }
//
// if (qName.equalsIgnoreCase("SEX")) {
// sex = true;
// }
//
// if (qName.equalsIgnoreCase("SALARY")) {
// salary = true;
// }
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// System.out.println("End Element :" + qName);
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
demo d = new demo();
if (name) {
// System.out.println("Name : " + new String(ch, start, length));
d.setName(new String(ch, start, length));
list.add(d);
name = false;
}
//
// if (age) {
// System.out.println("Age : " + new String(ch, start, length));
// d.setAge(new String(ch, start, length));
// age = false;
// }
//
// if (sex) {
// System.out.println("Sex : " + new String(ch, start, length));
// d.setSex(new String(ch, start, length));
// sex = false;
// }
//
// if (salary) {
// System.out.println("Salary : " + new String(ch, start, length));
// d.setSalary(new String(ch, start, length));
// salary = false;
// }

}
};
parser.parse(in, handler);
} catch (Exception e) {
e.printStackTrace();
}
return list;
}

// public static void main(String[] args) {
// try {
// List<demo> lists = ParserXMLWithSAX.parserXMl(new FileInputStream(Config.getPath()));
// for (demo dem : lists) {
// System.out.println("Name: " + dem.getName());
// }
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
// }
}

DOM4J方式

使用DOM4J时需要自己导入lib包,不过该方案的确很优秀,很多框架都是用它来作为XML文件的解析方案的。

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
package me.wenyue.canglang.androidxmltest.parserDAOImpl;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;

import me.wenyue.canglang.androidxmltest.entity.demo;
import me.wenyue.canglang.androidxmltest.util.Config;

/**
* Created by canglangwenyue on 15-6-26.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class ParserXMLWithDOM4J

// implements XMLParser
{
// @Override
public static List<demo> parserXMl(InputStream in) {
SAXReader reader = new SAXReader();
try {
Document document = reader.read(in);

System.out.println("Root element :"
+ document.getRootElement().getName());

//拿到全部信息
//List<Node> nodes = document.selectNodes("/users/user");
//拿到rollno为493的student标签下的信息
List<Node> nodes = document.selectNodes("/users/user[@id='1']");
System.out.println("----------------------------");
for (Node node : nodes) {
System.out.println("\nCurrent Element :"
+ node.getName());
System.out.println("id: "
+ node.valueOf("@id"));
System.out.println("Name : " + node.selectSingleNode("name").getText());
System.out.println("Age : " + node.selectSingleNode("age").getText());
System.out.println("Sex : " + node.selectSingleNode("sex").getText());
System.out.println("Salary : " + node.selectSingleNode("salary").getText());
}
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}

public static void main(String[] args) {
try {
parserXMl(new FileInputStream(Config.getPath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

XmlPullParser方式

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
package me.wenyue.canglang.androidxmltest.parserDAOImpl;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import me.wenyue.canglang.androidxmltest.entity.demo;
import me.wenyue.canglang.androidxmltest.parserDAO.XMLParser;

/**
* Created by canglangwenyue on 15-6-26.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class ParserXMLByPull
implements XMLParser {


static List<demo> list;
static demo de;
static String text;

public ParserXMLByPull() {
list = new ArrayList<>();
}

@Override
public List<demo> parserXMl(InputStream in) {

XmlPullParserFactory factory = null;
XmlPullParser parser = null;

try {
factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
parser = factory.newPullParser();

parser.setInput(in, null);

int eventType = parser.getEventType();

while (eventType != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
if (tagName.equals("user")) {
System.out.println(parser.getAttributeName(1));
de = new demo();
}
break;
case XmlPullParser.TEXT:
text = parser.getText();
break;
case XmlPullParser.END_TAG:
if (tagName.equalsIgnoreCase("user"))
list.add(de);
if (tagName.equalsIgnoreCase("name"))
de.setName(text);
if (tagName.equalsIgnoreCase("age"))
de.setAge(text);
if (tagName.equalsIgnoreCase("sex"))
de.setSex(text);
if (tagName.equalsIgnoreCase("salary"))
de.setSalary(text);
break;
default:
break;
}
eventType = parser.next();
}

} catch (Exception e) {
e.printStackTrace();
}
return list;
}

}

最后,Config.java 用来存储文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package me.wenyue.canglang.androidxmltest.util;

/**
* Created by canglangwenyue on 15-6-25.
* Mail: b@canglangwenyue.com
* WebSite: canglangwenyue.com
*/

public class Config {
public static final String path = "/app/src/main/res/raw/demo.xml";

public static String getPath(){
String relativelyPath = System.getProperty("user.dir");
System.out.println(relativelyPath);
return relativelyPath+path;
}
}

接下来,就对比一下它们的优劣吧。

综合对比

DOM

DOM是基于树的结构,通常需要加载整文档和构造DOM树,然后才能开始工作。

优点:

  • 由于整棵树在内存中,因此可以对xml文档随机访问
  • 可以对xml文档进行修改操作
  • 较sax,dom使用也更简单。

    缺点:

  • 整个文档必须一次性解析完

  • 由于整个文档都需要载入内存,对于大文档成本高

SAX

SAX是基于事件驱动的,因此无需将整个文档载入内存,使用者只需要监听自己感兴趣的事件即可。

优点:

  • 无需将整个xml文档载入内存,因此消耗内存少
  • 可以注册多个ContentHandler

    缺点:

  • 不能随机的访问xml中的节点

  • 不能修改文档

JDOM

JDOM是纯Java的处理XML的API,其API中大量使用Collections类,

优点:

  • DOM方式的优点
  • 具有SAX的Java规则

    缺点

    *与DOM方式类似

    DOM4J

    在xml解析方面是最优秀的,且支持XPAth,易用性高,Hibernate为代表的诸多框架都是使用它。但是在android开发中可能会优点杀鸡用牛刀的感觉。

XmlPullParser只有在android使用,就不多说了。