摘要:xml 作为一种可扩展的标记语言出现,其相对于html,主要是用于数据的保存,和数据结构信息的描述,标记格式并不固定,可以自行定制,但是必须要嵌套出现。下面介绍XML的基本格式和语法以及4种解析方式。

XML文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<users>
<user id="1">
<name>Andrea</name>
<age>10</age>
<gender>Male</gender>
</user>
<user id="2">
<name>Bell</name>
<age>20</age>
<gender>Male</gender>
</user>
<user id="3">
<name>Calinda</name>
<age>30</age>
<gender>Female</gender>
</user>
</users>

上面即使基本的XML文件,一个XML文件分为头信息数据区

头信息即上述XML文件的第一行,<?xml version="1.0" encoding="utf-8" ?>该头信息指定XML文件的版本和字符编码。

除去第一行之后的内容都属于本XML文件的数据区,即数据保存部分。其中根元素为users,又包含3个user元素,user元素有id属性和3个标签元素,分别是name,age和gender。

XML解析

下面介绍XML文件的四种解析方式,DOM方式,SAX解析,JDOM方式以及DOM4J方式。

以简单工厂设计模式,先定义一个基本的XML解析接口作为产品接口,包含解析xml,和生成xml文件两个抽象方法,再定义4个具体的产品类即4种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
/**
* ParserMethod enum
*/
enum ParserMethod{
DOM,SAX,JDOM,DOM4J
}

/**
* HelperFactory to manufacture XmlHelper
* a static method is needed
*/
class XmlHelperFactory{
public static XmlHelper newInstance(ParserMethod method){
XmlHelper helper = null;
switch (method) {
case DOM:
helper = new DomHelper();
break;
case SAX:
helper = new SaxHelper();
break;
case JDOM:
helper = new JDomHelper();
break;
case DOM4J:
helper = new Dom4JHelper();
break;
default:
break;
}
return helper;
}
}

/**
* Base XmlHelper interface
*/
interface XmlHelper{
void parseXml(String fileName)throws Exception ;
void writeXml(String fileName) throws Exception ;
}

DOM解析

第一种即为W3C推荐的标准,DOM解析方式,以DOM树的形式将整个XML文件结构装载到内存中。

要知道对于一个大而复杂的XML文件,在内存中建立它的DOM树形式是非常耗费空间的,因此DOM解析方式适合解析那些XML文件较小,内容需要经常修改,结构较为简单的。

对于小XML文件,使用DOM解析方式,非常简单,且易于理解,由于DOM树存于内存中,支持随意读取,对于上述XML文件,只要将其想象成一颗树,根元素为users。解析代码如下:

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
/**
* DomHelper, a concrete XmlHelper product
*/

class DomHelper implements XmlHelper {
private DocumentBuilder builder = null;

public DomHelper(){
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
}catch(Exception e){

}
}

public void parseXml(String fileName) throws Exception {
Document document = builder.parse(fileName);
NodeList nodes = document.getChildNodes();//document.getElementsByTagName("users");

for(int i=0;i<nodes.getLength();i++){
Node users = nodes.item(i);
NodeList user = users.getChildNodes();

for(int j=0;j<user.getLength();j++){
Node userInfo = user.item(j);
NamedNodeMap userAttr = userInfo.getAttributes();

if(userAttr != null && userAttr.getLength() > 0){
for(int at=0;at<userAttr.getLength();at++){
System.out.print(userAttr.item(at).getNodeName() +
"=" + userAttr.item(at).getTextContent());

}
}

NodeList infoMeta = userInfo.getChildNodes();

for(int k=0;k<infoMeta.getLength();k++){
if(infoMeta.item(k).getNodeName() != "#text"){
System.out.print(infoMeta.item(k).getNodeName() +
":" + infoMeta.item(k).getTextContent());

}
System.out.print("\t");
}
System.out.println();
}
}
}


public void writeXml(String fileName) throws Exception{
Document document = builder.newDocument();

Node users = document.createElement("users");
Node user = document.createElement("user");
Node name = document.createElement("name");
Node age = document.createElement("age");
Node gender = document.createElement("gender");

name.appendChild(document.createTextNode("zhangsan"));
age.appendChild(document.createTextNode("20"));
gender.appendChild(document.createTextNode("Male"));
user.appendChild(name);
user.appendChild(age);
user.appendChild(gender);

users.appendChild(user);
document.appendChild(users);

TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING,"utf-8");

DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(fileName));
transformer.transform(source,result);
}
}

在主函数中使用该种解析方式,

1
2
3
4
5
6
7
8
public class TestXml{
public static void main(String[] args) throws Exception {
//Simple factory mode
XmlHelper helper = XmlHelperFactory.newInstance(ParserMethod.JDOM);
helper.parseXml("./example.xml");
helper.writeXml("./output.xml");
}
}

输出如下:

id=1 name:Andrea age:10 gender:Male
id=2 name:Bell age:20 gender:Male
id=3 name:Calinda age:30 gender:Female

SAX解析

第二种方式为SAX解析方式,也是W3C提供的xml解析标准,与DOM解析方式不同的是,SAX解析以事件驱动方式进行,顺序部分解析,还必须定义自己的事件处理器类,该类继承自DefaultHandler类。

主要方法有startDocument方法,文档开始自动调用,endDocument文档结束自动调用,startElement,元素开始自动调用(根元素和节点元素都是如此),endElement,元素结束自动调用。characters方法遇到具体的元素结点内部值会自动调用,并解析。

SAX解析方式不在乎文档大小,即时加载元素结点,存在于内存的东西相当少,因此SAX解析方式更加灵活,可以针对那些XML文档较大的解析,SAX易于读取,但不支持文件修改,因此writeXml方法无内容。

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
/**
* SaxHelper, another XmlHelper product
*/

class SaxHelper implements XmlHelper {
public void parseXml(String fileName) throws Exception{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(fileName,new MySaxHandler());
}

public void writeXml(String fileName) throws Exception {

}
}

class MySaxHandler extends DefaultHandler{
public void startDocument() throws SAXException {
System.out.println("<?xml version=\"1.0\" encoding=\"utf-8\">");
}

public void endDocument() throws SAXException{
System.out.println("document ended!");
}

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.print("<");
System.out.print(qName);

if(attributes.getLength() > 0){
for(int i=0;i<attributes.getLength();i++){
System.out.print(" "+attributes.getQName(i)+"=\"" + attributes.getValue(i)+"\"");
}
}
System.out.print(">");
}

public void endElement(String uri,String localName,String qName) throws SAXException {
System.out.print("</");
System.out.print(qName);
System.out.print(">");
}

public void characters(char[] ch, int start, int length){
System.out.print(new String(ch,start,length));
}
}

使用主函数中的parseXml进行解析,输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8">
<users>
<user id="1">
<name>Andrea</name>
<age>10</age>
<gender>Male</gender>
</user>
<user id="2">
<name>Bell</name>
<age>20</age>
<gender>Male</gender>
</user>
<user id="3">
<name>Calinda</name>
<age>30</age>
<gender>Female</gender>
</user>
</users>document ended!

JDOM解析

第三种方式JDOM方式,JDOM解析方式=DOM可以修改+SAX大文件读取,相对DOM解析方式,更加简单灵活,统一以Element组织。

需要注意的是以下的JDOM和DOM4J解析都需要额外的jar包支持,下载之后放到某个目录,设置CLASSPATH环境变量即可使用。

这两个jar包都非常容易下载到,所以这里就不再提供了,具体版本可根据你需要选择,一般不会有太大差别。

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
/**
* JDomHelper, another XmlHelper product
*/

class JDomHelper implements XmlHelper {
public void parseXml(String fileName) throws Exception {
SAXBuilder builder = new SAXBuilder();
org.jdom2.Document document = builder.build(new File(fileName));

Element users = document.getRootElement();
List<Element> userList = users.getChildren();

for(int i=0;i<userList.size();i++){
Element user = (Element)userList.get(i);
List<Attribute> userAttr = user.getAttributes();

for(int j=0;j<userAttr.size();j++){
Attribute attr = userAttr.get(j);
System.out.print(attr.getName()+":"+attr.getValue());
}
System.out.println();

List<Element> userInfo = user.getChildren();
for(int k=0;k<userInfo.size();k++){
Element userMeta = userInfo.get(k);
System.out.println(userMeta.getName()+":"+userMeta.getValue());
}
System.out.println();
}
}

public void writeXml(String fileName) throws Exception {
Element users = new Element("users");
Element user = new Element("user");
Element name = new Element("name");
Element age = new Element("age");
Element gender = new Element("gender");

Attribute id = new Attribute("id","1");
name.setText("zhangsan");
age.setText("10");
gender.setText("Male");

user.setAttribute(id);
user.addContent(name);
user.addContent(age);
user.addContent(gender);

users.addContent(user);

org.jdom2.Document document = new org.jdom2.Document(users);
XMLOutputter out = new XMLOutputter();
out.setFormat(out.getFormat().setEncoding("utf-8"));
out.output(document,new FileOutputStream(new File(fileName)));
}
}

DOM4J解析

第四种方式为DOM4J方式,Hibernate和Spring等框架都采用DOM4J方式支持XML文件的解析。

生成xml文件时,Document对象是依靠DocumentHelper的createDocument完成

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
/**
* Dom4JHelper, another XmlHelper product
*/

class Dom4JHelper implements XmlHelper
public void parseXml(String fileName) throws Exception{
SAXReader reader = new SAXReader();
org.dom4j.Document document = reader.read(new File(fileName));
org.dom4j.Element users = document.getRootElement();
Iterator<org.dom4j.Element> userIter = users.elementIterator();

while (userIter.hasNext()) {
org.dom4j.Element user = userIter.next();
List<org.dom4j.Attribute> userAttr = user.attributes();
for(int i=0;i<userAttr.size();i++){
org.dom4j.Attribute attr = userAttr.get(i);
System.out.println(attr.getName()+":"+attr.getValue());
}
Iterator<org.dom4j.Element> infoIter = user.elementIterator();
while(infoIter.hasNext()){
org.dom4j.Element info = infoIter.next();
System.out.println(info.getName()+":"+info.getText());
}
System.out.println();
}
}

public void writeXml(String fileName) throws Exception {
org.dom4j.Document document = DocumentHelper.createDocument();
org.dom4j.Element users = document.addElement("users");
org.dom4j.Element user = users.addElement("user");
org.dom4j.Element name = user.addElement("name");
org.dom4j.Element age = user.addElement("age");
org.dom4j.Element gender = user.addElement("gender");

user.addAttribute("id", "1");
name.setText("zhangsan");
age.setText("20");
gender.setText("Male");

OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("utf-8");
XMLWriter writer = new XMLWriter(new FileOutputStream(new File(fileName)));
writer.write(document);
writer.close();
}
}