XML编程(用Java编写解析器)之 DOM解析

一、Java解析XML概述

1. XML解析方式分为两种:DOM方式和SAX方式

  • DOM:Document Object Model,文档对象模型。这种方式是W3C推荐的处理XML的一种方式。
  • DOM解析:1.要生成节点树,就必须加载整个文档。2.容易造成内存溢出。
  • SAX:Simple API for XML。这种方式不是官方标准,属于开源社区XML-DEV,几乎所有的XML解析器都支持它。
  • SAX解析:1.读取单个节点,再去触发事件,占用内存小。2.不适合做修改,只适合做XML文档的读取。

2. XML解析开发包

  • JAXP:是SUN公司推出的解析标准实现。
  • Dom4J:是开源组织推出的解析开发包。
  • JDom:是开源组织推出的解析开发包。

二、JAXP

1. JAXP:(Java API for XML Processing)开发包是JavaSE的一部分,它由一下几个包及其子包组成:

  • org.w3c.dom:提供DOM方式解析XML的标准接口
  • org.xml.sax:提供SAX方式解析XML的标准接口
  • javax.xml:提供了解析XML文档的类

2. javax.xml.parsers包中,定义了几个工厂类。我们可以通过调用这些工厂类,得到对XML文档进行解析的DOM和SAX解析器对象。

  • DocumentBuilderFactory
  • SAXParserFactory

三、使用JAXP进行DOM解析

javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 , DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。

四、获得JAXP中的DOM解析器

  • 调用DocumentBuilderFactory.newInstance()方法得到创建DOM解析器的工厂
  • 调用工厂对象的newDocumentBuilder方法得到DOM解析器对象
  • 调用DOM解析器对象的parse()方法解析XML文档,得到代表整个文档的Document对象,进而可以利用DOM特性对整个XML文档进行操作了。

src/book.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?><书架>
	<书>
		<书名>你所谓的稳定,不过是在浪费生命</书名>
		<作者>李尚龙</作者>
		<售价>32</售价>
	</书>
	<书>
		<书名>你要么出众,要么出局</书名>
		<作者>李尚龙2</作者>
		<售价>27</售价>
	<批发价>12</批发价></书>
</书架>

com.it.utils.DemoUtil.java

package com.it.utils;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

public class DemoUtil {
	/**
	 * 读取XML,返回一个Document对象
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public static Document getDocument(String path) throws Exception{
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = dbFactory.newDocumentBuilder();
		Document document = builder.parse("src/books.xml");
		return document;
	}
	
	/**
	 * 将一个Document文档对象从内存中写入到指定的文件中
	 * @param document
	 * @param path
	 * @throws Exception
	 */
	public static void writeXmlToFile(Document document, String path) throws Exception{
		Transformer tf = TransformerFactory.newInstance().newTransformer();
		tf.transform(new DOMSource(document), new StreamResult(path));
	}
}

com.it.test.DemoTest.java

package com.it.test;

import java.io.IOException;

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

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

import com.it.utils.DemoUtil;

public class DomTest {
	public static void main(String[] args) throws Exception {
		test2();

	}

	public static void test1() throws Exception {
		Document document = DemoUtil.getDocument("src/books.xml");
		// 2.找节点
		NodeList nodelist = document.getElementsByTagName("作者");
		Node author = nodelist.item(0);
		// 3.找文本输出
		System.out.println(author.getTextContent());
	}
	
	public static void test2() throws Exception{
		//1.得到document
		Document document = DemoUtil.getDocument("src/books.xml");
		//2.创建新的批发价节点并赋内部文本值
		Element bookElement = document.createElement("批发价");
		bookElement.setTextContent("12");
		//3.找到第二本书,并追加子节点
		Node secondBook = document.getElementsByTagName("书").item(1);
		secondBook.appendChild(bookElement);
		//3.写回XML
		DemoUtil.writeXmlToFile(document, "src/books.xml");
	}

}

五、DOM编程

DOM模型(Document Object Model)

1. DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点)。

2. 在DOM中,节点之间关系如下:

  • 位于一个节点之上的节点是该节点的父节点(parent)
  • 一个节点之下的节点是该节点的子节点(children)
  • 同一层次,具有相同父节点的节点是兄弟节点(sibling)
  • 一个节点的下一个层次的节点集合时节点后代(descendant)
  • 父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)

3. 节点类型

  • Node对象提供了一系列常量来代表节点的类型,当开发人员获得某个Node类型后,就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。
  • Node对象提供了相应的方法去获得它的父节点或子节点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容了。

六、DOM方式解析XML文件

DOM解析编程

  • 得到某个具体的节点内容
  • 遍历所有元素节点
  • 修改某个元素节点的主体内容
  • 向指定元素节点中添加子元素节点
  • 向指定元素节点上增加同级元素节点(父节点.insertBefore(new,old))
  • 删除指定元素节点
  • 操作XML文件属性

七、更新XML文档

  • javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出,例如把xml文件应用样式表转成一个HTML文档。利用这个对象,当然也可以把Document对象又重新写入到XML文件中。
  • Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以javax.xml.transform.dom.DOMSource类来关联要转换的document对象。用javax.xml.transform.stream.StreamResult对象来表示数据的目的地。
  • Transformer对象通过TransformerFactory获得。

八、SAX解析

  • 在使用DOM解析XML文档时,需要读取整个XML文档,在内存中构架代表整个DOM树的Document对象,从而再对XML文档进行操作。此种情况下,如果XML文档特别大,就会消耗计算机的大量内存,并且容易导致内存溢出。
  • SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才会对文档进行操作。
  • SAX采用事件处理的方式解析XML文件,利用SAX解析XML文档,涉及两个部分:解析器和事件处理器。
  • 解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
  • 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
  • 事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
  • 阅读ContentHandler API文档,常用方法:startElement、endElement、characters

九、SAX方式解析XML文档

  • 使用SAXParserFactory创建SAX解析工厂
  • SAXParserFactory spf = SAXParserFactory.newInstance();
  • 通过SAX解析工厂得到解析器对象
  • SAXParser sp = spf.newSAXParser();
  • 通过解析器对象得到一个XML的读取器
  • XMLReader xmlReader = sp.getXMLReader();
  • 设置读取器的时间处理器
  • xmlReader.setContentHandler(new BookParserHandler());
  • 解析xml文件
  • xmlReader.parse("book.xml");

十、SAX解析编程

方式一:

package www.it.sax;

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

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

public class SaxTest {
	public static void main(String[] args) throws Exception {
		//得到SAXParserFactory实例
		SAXParserFactory factory = SAXParserFactory.newInstance();
		//由工厂创建SAXParserFactory解析器
		SAXParser parser = factory.newSAXParser();
		//
		parser.parse("src/books.xml", new DefaultHandler(){

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

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

			@Override
			public void startElement(String uri, String localName, String qName, Attributes attributes)
					throws SAXException {
				System.out.println("元素开始了");
			}

			@Override
			public void endElement(String uri, String localName, String qName) throws SAXException {
				System.out.println("元素结束了");
			}

			@Override
			public void characters(char[] ch, int start, int length) throws SAXException {
				System.out.println(new String(ch,start,length));
			}
			
		});
	}
}

方式二:封装数据到Javabean中

Book.java

package www.it.domain;

public class Book {
	private String bookName;
	private String author;
	private double price;

	public Book() {
		super();
	}

	public Book(String bookName, String author, double price) {
		super();
		this.bookName = bookName;
		this.author = author;
		this.price = price;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", author=" + author + ", price=" + price + "]";
	}

}

SaxTest.java

package www.it.sax;

import java.util.ArrayList;
import java.util.List;

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

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

import www.it.domain.Book;

public class SaxTest2 {
	public static void main(String[] args) throws Exception {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		// 得到XMLReader对象
		XMLReader reader = parser.getXMLReader();
		final List<Book> books = new ArrayList<Book>();
		// 设置内容处理器
		reader.setContentHandler(new DefaultHandler() {
			Book book = null;
			String node = null;

			public void startElement(String uri, String localName, String qName, Attributes attributes)
					throws SAXException {
				if ("书".equals(qName)) {
					book = new Book();
				}
				node = qName;
			}

			public void endElement(String uri, String localName, String qName) throws SAXException {
				if ("书".equals(qName)) {
					books.add(book);
					book = null;
				}
				node = null;
			}

			public void characters(char[] ch, int start, int length) throws SAXException {
				if ("书名".equals(node)) {
					book.setBookName(new String(ch, start, length));
				}
				if ("作者".equals(node)) {
					book.setAuthor(new String(ch, start, length));
				}
				if ("售价".equals(node)) {
					book.setPrice(Double.parseDouble(new String(ch, start, length)));
				}

			}

		});
		
		//加载xml
		reader.parse("src/books.xml");
		//遍历集合
		for(Book b : books){
			System.out.println(b);
		}
	}
}

十一、DOM4J解析XML文档

  • DOM4J是一个简单、灵活的开放源代码的库。DOM4J是由早起开发JDOM的人分离出来而后独立开发的。与JDOM不同的是,DOM4J使用接口和抽象基类,虽然DOM4J的API相对要复杂一些,但它提供了比JDOM更好的灵活性。
  • DOM4J是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点。现在很多软件采用DOM4J,例如Hibernate,包括Sun公司自己的JAXM也用了DOM4J。
  • 使用DOM4J开发,需要下载DOM4J相应的jar文件。

十二、Document对象

在DOM4J中,获得Document对象的方式有三种:

1. 读取XML文件,获得document对象

SAXReader reader = new SAXReader();   

Document document = reader.read(new File("input.xml"));

2. 解析XML形式的文本,得到document对象

 

String text = "<members></members>" 

Document document = DocumentHelper.parseText(text);

 3. 主动创建document对象

 

Document document = DocumentHelper.createDocument();

//创建根节点

Element root = document.addElement("members"); 

十三、节点对象

1. 获取文档根节点

Element root = document.getRootElement();

2. 取得某个节点的子节点

Element element = node.element("书名"); 

3. 取得节点的文字

String text = node.getText(); 

4. 取得某个节点下所有名为“member”的子节点,并进行遍历

List nodes = rootElement.elements("member");

for(Iterator it = nodes.iterator();it.hasNext();){

        Element ele = (Element)it.next();

        //do something

}

5. 对某节点下的所有子节点进行遍历

for(Iterator it = root.elementIterator();it.hasNext()){

         Element element = (Element)it.next();

}

6. 在某节点下添加子节点

 Element ageElm = newMemberElm.addElement("age");

7. 设置节点文字

element.setText("29");

8. 删除某节点

//childElm是待删除的节点,parentElm是其父节点

parentElm.remove(childElm);

9. 添加一个CDATA节点

 Element contentElm = infoElm.addElement("content");

 contentElm.add(diary.getContent());

十四、节点对象属性

1. 取得某节点下的某属性

 Element root = document.getRootElement();

//属性名name

Attribute attribute = root.attribute("size");

2. 取得属性的文字

String text = attribute.getText();

3. 删除某属性

Attribute attribute = root.attribute("size");

root.remove(attribute);

4. 遍历某节点的所有属性

Element root = document.getRootElement();

for(Iterator it = root.attributeIterator();it.hasNext();){

          Attribute attribute = (Attribute)it.next();

         String text = attribute.getText();

         System.out.println(text);

}

5. 设置某节点的属性和文字

newMemberElm.addAttribute("name","sitinspring");

6. 设置属性的文字

Attribute attribute = root.attribute("name");

attribute.setText("sitinspring");

十五、将文档写入XML文件

1. 文档中全为英文,不设置编码,直接写入的形式

XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));

writer.write(document);

writer.close();

2. 文档中含有中文,设置编码格式写入的形式

OutputFormat format = OutputFormat.createPrettyPrint();

//指定XML编码

format.setEncoding("GBK");

XMLWriter writer = new XMLWriter(new FileOutputStream("output.xml").format);

writer.write(document);

writer.close();

十六、DOM4J在指定位置插入节点

1. 得到插入位置的节点列表(list)

2. 调用list.add(index,element),由index决定element的插入位置。

3. Element元素可以通过DocumentHelper对象得到,示例代码:

Element aaa = DocumentHelper.createElement("aaa");

aaa.setText("aaa");

List list = root.element("书").elements();

list.add(1,aaa);

//更新document

十七、字符串与XML的转换

1. 将字符串转换为XML

String text = "<members><member>sitinspring</member></members>";

Document document = DocumentHelper.parseText(text);

2. 将文档或节点的XML转换为字符串

SAXReader reader = new SAXReader();

Document document = reader.read(new File("input.xml"));

Element root = document.getRootElement();

String docXmlText = document.asXML();

String rootXmlText = root.asXML();

Element memberElm = root.element("member");

String memberXmlText = memberElm.asXML();

十八、示例代码

Dom4JUtil

package com.it.utils;

import java.io.FileOutputStream;
import java.io.IOException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

public class Dom4JUtil {
	/**
	 * 用于将一个XML文档转换成Document对象
	 * @return
	 * @throws DocumentException
	 */
	public static Document getDocument() throws DocumentException{
		SAXReader reader = new SAXReader();
		return reader.read("src/books.xml");
	}
	
	/**
	 * 将一个Document写入到磁盘
	 * @param document
	 * @throws IOException
	 */
	public static void writeDocumentToXml(Document document) throws IOException{
		XMLWriter writer = new XMLWriter(new FileOutputStream("src/books.xml"), OutputFormat.createPrettyPrint());
		writer.write(document);
	}
}

Dom4JTest

package com.it.dom4j.test;

import java.io.IOException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.junit.Test;

import com.it.utils.Dom4JUtil;

public class Dom4jTest {

	/**
	 * 1.得到具体的节点内容------->第二本书的作者
	 */
	@Test
	public void test1(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.得到根节点
			Element root = document.getRootElement();
			//3.找第二本书
			Element node = (Element) root.elements().get(1);
			//4.找第二本书的作者
			Element author = node.element("作者");
			//5.读取文本
			String authorText = author.getText();
			System.out.println(authorText);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 2.遍历所有元素节点
	 */
	@Test
	public void test2(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.得到根节点
			Element root = document.getRootElement();
			//3.调用
			treeWalk(root);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void treeWalk(Element ele){
		System.out.println(ele.getName());
		for(int i = 0; i < ele.nodeCount(); i++){
			Node node = ele.node(i);
			if(node.getNodeType() == Node.ELEMENT_NODE){
				treeWalk((Element)node);
			}
		}
	}
	
	/**
	 * 修改某个元素节点的主体内容----->第一本书的售价改为35
	 */
	@Test
	public void test3(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.得到根节点
			Element root = document.getRootElement();
			//3.找到第一本书的节点
			Element bookEle = root.element("书");
			//4.找到售价节点
			Element saleEle = bookEle.element("售价");
			//5.设置售价节点文本值
			saleEle.setText("35");
			//6.写回XML
			Dom4JUtil.writeDocumentToXml(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 向指定元素节点中增加子元素节点------>在第二本书的子节点中添加批发价节点
	 */
	@Test
	public void test4(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.得到根节点
			Element root = document.getRootElement();
			//3.找到第二本书
			Element bookEle = (Element)root.elements().get(1);
			//4.添加节点并设置文本
			bookEle.addElement("批发价").setText("12");
			//5.写回XML
			Dom4JUtil.writeDocumentToXml(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 向指定元素节点上增加同级元素节点---->第一本书售价前面添加一个内部价节点
	 */
	@Test
	public void test5(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.得到根节点
			Element root = document.getRootElement();
			//3.找到第一本书
			Element bookEle = root.element("书");
			//4.创建一个节点
			Element innerPriceElement = DocumentHelper.createElement("内部价");
			innerPriceElement.setText("15");
			//5.将创建的节点插入到指定位置
			bookEle.elements().add(2, innerPriceElement);
			//6.写回XML
			Dom4JUtil.writeDocumentToXml(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 删除指定元素节点----->删除第一本书的内部价节点
	 */
	@Test
	public void test6(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.找到根节点
			Element root = document.getRootElement();
			//3.找到第一本书
			Element bookEle = root.element("书");
			//4.找到内部价节点
			Element innerPriceEle = bookEle.element("内部价");
			//5.删除内部价节点
			bookEle.remove(innerPriceEle);
			//6.写回XML
			Dom4JUtil.writeDocumentToXml(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	//7.操作XML文件属性---->在第二本书的书节点中,添加一个bookID属性
	@Test
	public void test7(){
		try {
			//1.得到Document对象
			Document document = Dom4JUtil.getDocument();
			//2.找到根节点
			Element root = document.getRootElement();
			//3.找到第二本书
			Element bookEle = (Element)root.elements().get(1);
			//4.设置属性
			bookEle.addAttribute("bookID", "isb-111");
			//5.取节点的属性
			String str = bookEle.attributeValue("bookID");
			System.out.println(str);
			//写回到XML
			Dom4JUtil.writeDocumentToXml(document);
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

十九、Xpath与DOM4J

参考学习文档:XpathTutorial。功能强大,找节点非常快。

使用前需要导包。

猜你喜欢

转载自blog.csdn.net/Ada_yangyang/article/details/81366917
今日推荐