xml基础及其解析xml文档
xml基础语法
一个基本的xml构成:
<!--version指的是xml为版本,将其写死为1.0即可-->
<!--encoding 代表是文档的编码声明 通常我们使用的是utf-8 这个声明使得平台在打开xml文件时使用的utf-8进行解码-->
<?xml version="1.0" encoding="utf-8" ?>
<!--xml语法中,根节点要求是唯一的-->
<根节点>
<!--多个子节点的申明-->
<子节点>
</子节点>
</根节点>
注意:xml标签区分大小写,并且xml标签不能以数字开头,标签名中间不能使用空格,下面是几种错误的写法:
<2contach>
<!--标签不能以数字开头-->
</2contach>
<contact list>
<!--错误的标签使用方法,不能再标签中使用空格-->
</contact list>
<Contact>
<!--标签区分大小写,并且要正确的配对,显然该标签不能配对-->
</contact>
中国特色乱码问题
首先出现乱码一定是由于编码与解码采用的标准不一样,我们能看到的中文等都属于字符,从字符变成字节的过程我们成为编码,从字节变成字符的过程我们称之为解码,对于chrome浏览器,默认使用的是utf-8编码进行解码,当然可以自己设置,但是由于我们的编写xml文件的工具不同,导致可能进行编码采用的不是utf-8,所以会出现编码与解码采用的码表是不一样的。
- 写完xml进行保存的时候(也就是将整个xml文件保存到硬盘中,保存到硬盘中要进行编码成为二进制的10格式),实际上也就是编码过程(保存时使用utf-8)
- 打开或者解析xml文件的时候,实际上也就是解码过程(解码时采用utf-8)
上述的两个地方应该保持一致才不会出现乱码。注意在Windows上默认的编码格式ANSI在中国的计算机平台上实际上就是GBK编码。
写xml文件的工具
- 记事本、EditPlus等 注意保存时使用utf-8编码
- MyEclipse 如果在MyEclipse工具开发xml文件,保存xml文件时会按照文档声明的encoding来保存xml文件
- workbench 一个开源的可视化xml生成器
xml中使用的转义字符
在xml文件中内置了一些特殊的字符,这些特殊字符不能直接被浏览器原样输出,如果希望把这些字符按照浏览器的原样输出,那么就需要使用转义字符来实现。
<?xml version="1.0" encoding="utf-8" ?>
<codes>
<code>
<!--下面的部分我希望其按照html的原样输出-->
<!-- <p>段落</p> -->
<p$gt; 段落 </p>
<code>
<!--我们想把下面的内容都原样输出,怎么办?仍然全部采用转义字符吗?写的太复杂,又简单的方法,使用CDATA块-->
<!--CDATA块的作用:直接对包含一些特殊字符的内容统一进行原样输出,避免了一个个的进行转义-->
<!--CDATA语法如下:将需要转义的内容包起来即可-->
<![CDATA[ <html> <head> <title>这是一个xml文件</title> </head> <body> </body> </html> ]]>
</code>
</code>
</codes>
常用的特殊字符 转义字符
< < > >
'' "
& &
空格
处理指令–已经过时
处理指令的作用:告诉解析器怎样去解析该xml文档,用的最多的就是下面的一个申明,但是实际上也根本不用。
<?xml-stylesheet type="text/css" href="a.css" ?>
需要提取xml的内容,可以使用xml-stylesheet指令。
<?xml version="1.0" encoding="utf-8" ?>
<!--不引用任何的css文件,此时就能提取出所有的信息,标签除外,此种情况会将所有除标签之外的内容提出出来,排列在一行,当然我们可指定css文件改变这种布局-->
<?xml-stylesheet type="text/css" href="" ?>
<codes>
<code>
<!--下面的部分我希望其按照html的原样输出-->
<!-- <p>段落</p> -->
<p$gt; 段落 </p>
<code>
<!--我们想把下面的内容都原样输出,怎么办?仍然全部采用转义字符吗?写的太复杂,又简单的方法,使用CDATA块-->
<!--CDATA块的作用:直接对包含一些特殊字符的内容统一进行原样输出,避免了一个个的进行转义-->
<!--CDATA语法如下:将需要转义的内容包起来即可-->
<![CDATA[ <html> <head> <title>这是一个xml文件</title> </head> <body> </body> </html> ]]>
</code>
</code>
</codes>
我们引入css文件,改变默认抽取出来排放的规则。
<?xml version="1.0" encoding="utf-8" ?>
<!--不引用任何的css文件,此时就能提取出所有的信息,标签除外,此种情况会将所有除标签之外的内容提出出来,排列在一行,当然我们可指定css文件改变这种布局-->
<?xml-stylesheet type="text/css" href="a.css" ?>
<contactlist>
<contect id="001">
<name>张三</name>
<telephone>120</telephone>
</contect>
<contect id="002">
<name>李四</name>
<telephone>110</telephone>
</contect>
</contactlist>
a.css
/*这种控制方式就相当于在html中一样的写法,xml也可以作为网页的布局功能,但是根本不推荐这么去做,早期想要使用xml去取代html,但是html5出现之后,不用xml去替代*/
contect{ font-size:20px; font-color:red; width:100px; height:100px; display:block; margin-top:40px; background-color:green; }
上面的功能在xml中已经不再使用,但是xml仍然保留了这种功能,一般不会使用时,不要去刻意使用,如果要用到再去学就行了。
xml的两个重要的功能
下面两个作用在Java Web的开发中普遍使用
xml注释
<!--注释的内容-->
xml解析–Java应用程序读取xml文件的内容
xml文件除了给开发者看,但是更多的时候是使用程序去读取xml的内容,此时我们称为xml的解析。
xml解析原理
关于xml解析原理我们使用下面的xml代码进行使用和解析。
<?xml version="1.0" encoding="utf-8" ?>
<contactlist>
<contact id="001">
<name>张三</name>
<telephone>15338607192</telephone>
<age>20</age>
<email>zhangsan@qq.com</email>
<qq>13101900</qq>
</contact>
<contact id="002">
<name>李四</name>
<telephone>15338607193</telephone>
<age>20</age>
<email>lisi@qq.com</email>
<qq>13101901</qq>
</contact>
<contact id="003">
<name>王五</name>
<telephone>15338607194</telephone>
<age>21</age>
<email>wangwu@qq.com</email>
<qq>13101902</qq>
</contact>
</contactlist>
xml解析器在解析xml文档时,把xml文件的各个部分封装成对象,通过这些对象去操作xml文档,这个过程叫做DOM编程。解析的方式有且只有两种(从原理的角度上讲):
- DOM解析原理:xml解析器一次性将整个xml文档加载进内存,然后在内存中构建一棵Document对象树,通过Document对象可以得到树上的节点对象,通过节点对象就可以操作整个xml文档的内容。
在读取xml文档之后,会在内存中形成一棵DOM树,xml文件中的标签作为DOM树的节点,并且xml文档的根节点作为DOM树的根节点,所有的标签构成了一棵具有层次的树。节点存在一些信息:如节点名称、节点类型(标签节点、属性节点、文本节点、注释节点)。DOM解析是面向对象的编程。具体我们在使用时不用Node对象,而是使用其子类的三个对象。下面显示了DOM面向编程过程中常用的对象。
xml文档 —————-> Document对象 代表整个xml文档
节点 —————>Node对象 父类
标签节点 —————> Element对象 子类
属性节点 —————> Attribute对象 子类
文本节点 —————>Text对象 子类
那么我们怎么拿到这些对象信息呢?使用Document对象,它代表的是一个完整的xml文档,从而使得整个xml文档可以被读进去。
- SAX解析原理
xml解析工具
工具是某种原理之下具体的实现方式,基于上面的两种解析方式,出现了很多的工具,大致分为下面几个:
DOM解析工具
- JAXP(Oracle官方的工具) 没人用,难用
- JDOM(非官方的民间组织开发的工具) 也较为难用
- DOM4J(JDOM组织出来一批人开发的) 最为流行,最好用 三个框架(SSH)默认都使用的是DOM4J去读取xml内容
- 其它解析工具,不入流,不讨论
SAX解析工具
- Sax解析工具 (Oracle官方提供的解析工具)
DOM4J使用
DOM4J是基于DOM解析原理实现的xml文档解析工具,不包含在我们官方的JDK中,所以我们在使用的时候很关键的一点就是导包,导包,并且将其源码实现关联,以方便我们查看源代码,另外还有需要注意的是:使用第三方工具千万不能导错包,导包时要注意看清楚。
contact.xml
<?xml version="1.0" encoding="utf-8" ?>
<!--在整个xml文档中只能有一个根元素 在该xml文档中是contactlist -->
<contactlist>
<contact id="001">
<name>张三</name>
<telephone>15338607192</telephone>
<age>20</age>
<email>zhangsan@qq.com</email>
<qq>13101900</qq>
</contact>
<contact id="002">
<name>李四</name>
<telephone>15338607193</telephone>
<age>20</age>
<email>lisi@qq.com</email>
<qq>13101901</qq>
</contact>
<contact id="003">
<name>王五</name>
<telephone>15338607194</telephone>
<age>21</age>
<email>wangwu@qq.com</email>
<qq>13101902</qq>
</contact>
</contactlist>
DOM4J中核心API
- Element getRootElement(); //得到根标签
Iterator nodeIterator(); //得到一个节点的迭代器
- Element.attribute(“属性名”); //获取当前的属性对象
- Element.attributes(); //返回List对象
- Element.attributeIterator(); //返回一个迭代器
- Attribute.getName(); //拿到属性名
- Attribute.getValue(); //拿到属性值
将xml文档从磁盘读进内存,形成Document对象
testParseXml.java
package com.jpzhutech.xml;
import java.io.File;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.junit.Test;
public class ParseXml {
@Test
public void testParseXml(){
SAXReader reader = new SAXReader(); //创建一个xml文档的解析器
try {
Document document = reader.read(new File("./src/contact.xml")); //读取xml文档,并且返回Document对象
System.out.println(document); //输出为:org.dom4j.tree.DefaultDocument@2e3fe12e [Document: name file:///G:/Java/source/xml/./src/contact.xml]
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
}
读取所有的标签节点
将磁盘中的xml文档读进内存形成Document对象之后,此时我们就可以读取其中的标签、属性、文本等的内容了。
* 拿到所有的标签节点(Element) 使用nodeIterator方法进行循环迭代就能一层层的取出其中的标签节点
readXmlContext.java
package com.jpzhutech.xml;
import java.io.File;
import java.util.Iterator;
import org.dom4j.Branch;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
public class readXmlContext {
//主要目的用于得到节点的信息
@Test
public void test1(){
SAXReader reader = new SAXReader(); //获取reader用于将xml读进内存形成Document对象
try {
Document document = reader.read(new File("./src/contact.xml")); //其中.代表的是整个项目的根目录
Iterator<Node> nodeIterator = document.nodeIterator(); //得到节点的迭代器,返回的是所有的子节点对象只包含直接的子节点,不包含孙子节点甚至更为以下的节点
while(nodeIterator.hasNext()){
Node node = nodeIterator.next();
String name = node.getName();
System.out.println(name);
//继续取出下面的子节点,一定要明白,只有标签才有子节点,其它的比如属性、文本等都没有子节点
if(node instanceof Element){ //判断是否为节点,这样才能继续向下读取子节点
Element element = (Element) node; //如果节点为标签节点,将其进行强制类型转换为标签节点
Iterator<Node> nodeIterator2 = element.nodeIterator();
while(nodeIterator2.hasNext()){
Node next = nodeIterator2.next();
String name2 = next.getName();
System.out.println(name2);
if(next instanceof Element){
Element element2 = (Element)next;
Iterator<Node> nodeIterator3 = element2.nodeIterator();
while(nodeIterator3.hasNext()){
Node next2 = nodeIterator3.next();
String name3 = next2.getName();
System.out.println(name3);
}
}
}
}
}
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
}
稍微对上面的方法进行了改造,在获取其中的根标签时使用了getRootElement(),而不是把根标签当做一个普通的标签
package com.jpzhutech.xml;
import java.io.File;
import java.util.Iterator;
import org.dom4j.Branch;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
public class readXmlContext {
//主要目的用于得到节点的信息
@Test
public void test1(){
SAXReader reader = new SAXReader(); //获取reader用于将xml读进内存形成Document对象
try {
Document document = reader.read(new File("./src/contact.xml")); //其中.代表的是整个项目的根目录
Iterator<Node> nodeIterator = document.nodeIterator(); //得到节点的迭代器,返回的是所有的子节点对象只包含直接的子节点,不包含孙子节点甚至更为以下的节点
while(nodeIterator.hasNext()){
Node node = nodeIterator.next();
String name = node.getName();
System.out.println(name);
//继续取出下面的子节点,一定要明白,只有标签才有子节点,其它的比如属性、文本等都没有子节点
if(node instanceof Element){ //判断是否为节点,这样才能继续向下读取子节点
Element element = (Element) node; //如果节点为标签节点,将其进行强制类型转换为标签节点
Iterator<Node> nodeIterator2 = element.nodeIterator();
while(nodeIterator2.hasNext()){
Node next = nodeIterator2.next();
String name2 = next.getName();
System.out.println(name2);
if(next instanceof Element){
Element element2 = (Element)next;
Iterator<Node> nodeIterator3 = element2.nodeIterator();
while(nodeIterator3.hasNext()){
Node next2 = nodeIterator3.next();
String name3 = next2.getName();
System.out.println(name3);
}
}
}
}
}
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
@Test
public void test2(){
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("./src/contact.xml"));
Element rootElement = document.getRootElement(); //得到根标签,因为整个xml文档有且只有一个根标签
//System.out.println(rootElement);
//拿根节点下面的子节点(根标签下面的子标签)
getChildNodes(rootElement);
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
public void getChildNodes(Element element){
System.out.println(element.getName());
Iterator<Node> nodeIterator = element.nodeIterator();
while(nodeIterator.hasNext()){
Node next = nodeIterator.next();
String name = next.getName();
System.out.println(name);
if(next instanceof Element){
Element element2 = (Element)next;
getChildNodes(element2); //递归的将所有的节点读出来
}
}
}
}
package com.jpzhutech.xml;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Branch;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
public class readXmlContext {
//主要目的用于得到节点的信息
@Test
public void test1(){
SAXReader reader = new SAXReader(); //获取reader用于将xml读进内存形成Document对象
try {
Document document = reader.read(new File("./src/contact.xml")); //其中.代表的是整个项目的根目录
Iterator<Node> nodeIterator = document.nodeIterator(); //得到节点的迭代器,返回的是所有的子节点对象只包含直接的子节点,不包含孙子节点甚至更为以下的节点
while(nodeIterator.hasNext()){
Node node = nodeIterator.next();
String name = node.getName();
System.out.println(name);
//继续取出下面的子节点,一定要明白,只有标签才有子节点,其它的比如属性、文本等都没有子节点
if(node instanceof Element){ //判断是否为节点,这样才能继续向下读取子节点
Element element = (Element) node; //如果节点为标签节点,将其进行强制类型转换为标签节点
Iterator<Node> nodeIterator2 = element.nodeIterator();
while(nodeIterator2.hasNext()){
Node next = nodeIterator2.next();
String name2 = next.getName();
System.out.println(name2);
if(next instanceof Element){
Element element2 = (Element)next;
Iterator<Node> nodeIterator3 = element2.nodeIterator();
while(nodeIterator3.hasNext()){
Node next2 = nodeIterator3.next();
String name3 = next2.getName();
System.out.println(name3);
}
}
}
}
}
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
@Test
public void test2(){
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("./src/contact.xml"));
Element rootElement = document.getRootElement(); //得到根标签,因为整个xml文档有且只有一个根标签
//System.out.println(rootElement);
//拿根节点下面的子节点(根标签下面的子标签)
getChildNodes(rootElement);
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
public void getChildNodes(Element element){
System.out.println(element.getName());
Iterator<Node> nodeIterator = element.nodeIterator();
while(nodeIterator.hasNext()){
Node next = nodeIterator.next();
String name = next.getName();
System.out.println(name);
if(next instanceof Element){
Element element2 = (Element)next;
getChildNodes(element2); //递归的将所有的节点读出来
}
}
}
@Test
public void test3(){
SAXReader reader = new SAXReader();
Document document;
try {
document = reader.read(new File("./src/contact.xml"));
/*// 1. 得到根标签 Element rootElement = document.getRootElement(); //得到标签名称 String name = rootElement.getName(); System.out.println(name); */
/*//2. 得到根标签的子标签 Element element = rootElement.element("contact"); //得到当前标签下面指定名称的第一个子标签 System.out.println(element.getName());*/
/*//3. 拿到跟标签下面所有的子标签 Iterator<Element> elementIterator = rootElement.elementIterator("contact"); while(elementIterator.hasNext()){ Element next = elementIterator.next(); String name2 = next.getName(); System.out.println(name2); }*/
/*//4. 使用另外的迭代器方法 Iterator<Element> elementIterator2 = rootElement.elementIterator(); while(elementIterator2.hasNext()){ Element next = elementIterator2.next(); String name2 = next.getName(); System.out.println(name2); }*/
/*//使用list集合来存放所有的成员 List<Element> elements = rootElement.elements();*/
/* //遍历list的第一种方式,使用迭代器 Iterator<Element> iterator = elements.iterator(); while(iterator.hasNext()){ Element next = iterator.next(); System.out.println(next.getName()); }*/
/* //遍历list的第二种方式,使用for循环 for(int i = 0 ; i < elements.size(); i++){ Element element = elements.get(i); System.out.println(element.getName()); } */
/* //遍历list的第三种方式,增强for循环 for(Element element : elements){ System.out.println(element.getName()); } */
//拿到某一个指定标签的孙子标签
Element element = document.getRootElement().element("contact").element("name"); //只能一个个的获取,目前还没有很好的方法解决
System.out.println(element.getName())s;
} catch (DocumentException e) {
throw new RuntimeException(e);
} //将xml文档转换成一个Document对象
}
}
在输出中出现了很多的null,这是为什么呢?在后面获取文本标签时会说明原因。
读取所有的属性节点
@Test
public void test4(){
//得到属性标签以及属性值
SAXReader reader = new SAXReader();
try {
Document document = reader.read(new File("./src/contact.xml"));
//首先我想读取contact标签下面的id=001属性,想要获取属性就要先获得属性所在的标签对象
Element element = document.getRootElement().element("contact"); //得到属性所在的标签对象
/*//得到属性对象,得到指定名称的属性值 String attributeValue = element.attributeValue("id"); System.out.println(attributeValue);*/
/*//得到属性对象,得到该标签下所有的属性并获得其属性值 List<Attribute> attributes = element.attributes(); ListIterator<Attribute> listIterator = attributes.listIterator(); while(listIterator.hasNext()){ Attribute next = listIterator.next(); System.out.println(next.getName()); String value = next.getValue(); System.out.println(value); }*/
/*//使用迭代器获取指定标签下的所有的属性值 Iterator<Attribute> attributeIterator = element.attributeIterator(); while(attributeIterator.hasNext()){ Attribute next = attributeIterator.next(); String name = next.getName(); String value = next.getValue(); System.out.println(name+"="+value); }*/
/*//仍然是返回指定的属性 Attribute attribute = element.attribute("name"); String name = attribute.getName(); String value = attribute.getValue(); System.out.println(name + "=" + value);*/
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
读取所有的文本节点
@Test
public void test5(){
//获取文本内容,想要拿到文本首先要拿到标签
SAXReader reader = new SAXReader();
try {
Document document = reader.read("./src/contact.xml");
/*//首先获取标签 Element element = document.getRootElement().element("contact").element("name"); //拿到指定文本内容 String text = element.getText(); System.out.println(text);*/
String elementText = document.getRootElement().element("contact").elementText("telephone");
System.out.println(elementText);
} catch (DocumentException e) {
throw new RuntimeException(e);
}
}
解决上面提出的问题
实际上在xml文档中空格、换行都是算xml文档的内容,不能忽略掉,也就是说在读取的时候,如果空格、换行出现了,那么我们应该将其中的内容读出。