解析xml的两种方式
Dom
Dom解析会将整个xml文件加载到内存中,然后再逐个解析
package com.study.domtest;
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.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* DOM方式解析xml
*/
public class DOMTest {
public static void main(String[] args) {
//1、创建一个DocumentBuilderFactory的对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//2、创建一个DocumentBuilder的对象
try {
//创建DocumentBuilder对象
DocumentBuilder db = dbf.newDocumentBuilder();
//3、通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下
/*注意导入Document对象时,要导入org.w3c.dom.Document包下的*/
Document document = db.parse("books.xml");//传入文件名可以是相对路径也可以是绝对路径
//获取所有book节点的集合
NodeList bookList = document.getElementsByTagName("book");
//通过nodelist的getLength()方法可以获取bookList的长度
System.out.println("一共有" + bookList.getLength() + "本书");
//遍历每一个book节点
for (int i = 0; i < bookList.getLength(); i++) {
System.out.println("=================下面开始遍历第" + (i + 1) + "本书的内容=================");
//❤未知节点属性的个数和属性名时:
//通过 item(i)方法 获取一个book节点,nodelist的索引值从0开始
Node book = bookList.item(i);
//获取book节点的所有属性集合
NamedNodeMap attrs = book.getAttributes();
System.out.println("第 " + (i + 1) + "本书共有" + attrs.getLength() + "个属性");
//遍历book的属性
for (int j = 0; j < attrs.getLength(); j++) {
//通过item(index)方法获取book节点的某一个属性
Node attr = attrs.item(j);
//获取属性名
System.out.print("属性名:" + attr.getNodeName());
//获取属性值
System.out.println("--属性值" + attr.getNodeValue());
}
//❤已知book节点有且只有1个id属性:
/*
//前提:已经知道book节点有且只能有1个id属性
//将book节点进行强制类型转换,转换成Element类型
Element book1 = (Element) bookList.item(i);
//通过getAttribute("id")方法获取属性值
String attrValue = book1.getAttribute("id");
System.out.println("id属性的属性值为" + attrValue);
*/
//解析book节点的子节点
NodeList childNodes = book.getChildNodes();
//遍历childNodes获取每个节点的节点名和节点值
System.out.println("第" + (i+1) + "本书共有" + childNodes.getLength() + "个子节点");
for (int k = 0; k < childNodes.getLength(); k++) {
//区分出text类型的node以及element类型的node
if(childNodes.item(k).getNodeType() == Node.ELEMENT_NODE){
//获取了element类型节点的节点名
System.out.print("第" + (k + 1) + "个节点的节点名:" + childNodes.item(k).getNodeName());
//获取了element类型节点的节点值
System.out.println("--节点值是:" + childNodes.item(k).getFirstChild().getNodeValue());
// System.out.println("--节点值是:" + childNodes.item(k).getTextContent());
}
}
System.out.println("======================结束遍历第" + (i + 1) + "本书的内容=================");
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个NODE对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力
SAX
SAX是SIMPLE API FOR XML的缩写,与DOM比较而言,SAX是一种轻量型的方法采用逐行解析的解析方式
SAX在概念上与DOM完全不同。首先,不同于DOM的文档驱动,它是事件驱动的,也就是说,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。在XMLReader接受XML文档,在读入XML文档的过程中就进行解析,也就是说读入文档的过程和解析的过程是同时进行的,这和DOM区别很大
图
Book实体类
package com.study.saxtest.entity;
/**
* 用Book实体类代表XML文件中的"<book>...</book>"中整个元素
* 在遇到<book>标签,证明我们要存储新的book时需要创建Book对象
*/
public class Book {
private String id;
private String name;
private String author;
private String year;
private String price;
private String language;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
}
SAXParserHandler类
package com.study.saxtest.handler;
import java.util.ArrayList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.study.saxtest.entity.Book;
public class SAXParserHandler extends DefaultHandler{
/*注意DefaultHandler是org.xml.sax.helpers包下的*/
int bookIndex = 0;//设置全局变量,用来记录是第几本书
String value = null;
Book book = null;
private ArrayList<Book> bookList = new ArrayList<Book>();//保存book对象
public ArrayList<Book> getBookList() {
return bookList;
}
/**
* 用来标识解析开始
*/
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("SAX解析开始");
}
/**
* 用来标识解析结束
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("SAX解析结束");
}
/**
* 用来遍历xml文件的开始标签
* 解析xml元素
*/
@Override
public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
//调用DefaultHandler类的startElement方法
super.startElement(uri, localName, qName, attributes);
if (qName.equals("book")) {
bookIndex++;
//创建一个book对象
/*Book*/ book = new Book();
//开始解析book元素的属性
System.out.println("======================开始遍历第"+bookIndex+"本书的内容=================");
/* //❤已知节点的属性名时:比如已知id属性,根据属性名称获取属性值
String value = attributes.getValue("id");
System.out.print("book的属性值是:"+value);*/
//❤未知节点的属性名时,获取属性名和属性值
int num=attributes.getLength();
for(int i=0;i<num;i++){
System.out.print("book元素的第"+(i+1)+"个属性名是:"+attributes.getQName(i));
System.out.println("---属性值是:"+attributes.getValue(i));
if (attributes.getQName(i).equals("id")) {//往book对象中塞值
book.setId(attributes.getValue(i));
}
}
}else if (!qName.equals("book") && !qName.equals("bookstore")) {
System.out.print("节点名是:" + qName + "---");//此时qName获取的是节点名(标签)
}
}
/**
* 用来遍历xml文件的结束标签
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//调用DefaultHandler类的endElement方法
super.endElement(uri, localName, qName);
//判断是否针对一本书已经遍历结束
if (qName.equals("book")) {
bookList.add(book);//在清空book对象之前先保存
book = null;//把book清空,方便解析下一个book节点
System.out.println("======================结束遍历第"+bookIndex+"本书的内容=================");
}else if (qName.equals("name")) {
book.setName(value);
}
else if (qName.equals("author")) {
book.setAuthor(value);
}
else if (qName.equals("year")) {
book.setYear(value);
}
else if (qName.equals("price")) {
book.setPrice(value);
}
else if (qName.equals("language")) {
book.setLanguage(value);
}
}
/**
* 获取文本
* 重写charaters()方法时,
* String(byte[] bytes,int offset,int length)的构造方法进行数组的传递
* 去除解析时多余空格
*/
@Override
public void characters(char[] ch, int start, int length)throws SAXException {
/**
* ch 代表节点中的所有内容,即每次遇到一个标签调用characters方法时,数组ch实际都是整个XML文档的内容
* 如何每次去调用characters方法时我们都可以获取不同的节点属性?这时就必须结合start(开始节点)和length(长度)
*/
super.characters(ch, start, length);
/*String */value = new String(ch, start, length);//value获取的是文本(开始和结束标签之间的文本)
// System.out.println(value);//输出时会多出两个空格,是因为xml文件中空格与换行字符被看成为一个文本节点
if(!value.trim().equals("")){//如果value去掉空格后不是空字符串
System.out.println("节点值是:" + value);
}
}
/**
* qName获取的是节点名(标签)
* value获取的是文本(开始和结束标签之间的文本)
* 思考:qName和value分别在两个方法中,如何将这两个方法中的参数整合到一起?
* 分析:要在两个方法中用同一个变量,就设置成全局变量,可以赋初值为null。
* 可以把characters()方法中的value作成一个全局变量
*
* 然后在endElement()方法中对book对象进行塞值。记得要把Book对象设置为全局变量,变量共享
*/
}
测试类:SAXTest
package com.study.saxtest.test;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
import com.study.saxtest.entity.Book;
import com.study.saxtest.handler.SAXParserHandler;
/**
* sax方式解析XML
*/
public class SAXTest {
public static void main(String[] args) {
//1.获取一个SAXParserFactory的实例对象
SAXParserFactory factory = SAXParserFactory.newInstance();
//2.通过factory的newSAXParser()方法获取一个SAXParser类的对象。
try {
SAXParser parser = factory.newSAXParser();
//创建SAXParserHandler对象
SAXParserHandler handler = new SAXParserHandler();
parser.parse("books.xml", handler);
System.out.println("~~~~~共有"+handler.getBookList().size()+"本书");
for (Book book : handler.getBookList()) {
System.out.println(book.getId());
System.out.println(book.getName());
System.out.println(book.getAuthor());
System.out.println(book.getYear());
System.out.println(book.getPrice());
System.out.println(book.getLanguage());
System.out.println("----finish----");
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
SAX解析开始
======================开始遍历第1本书的内容=================
book元素的第1个属性名是:id---属性值是:1
节点名是:name---节点值是:冰与火之歌
节点名是:author---节点值是:乔治马丁
节点名是:year---节点值是:2014
节点名是:price---节点值是:89
======================结束遍历第1本书的内容=================
======================开始遍历第2本书的内容=================
book元素的第1个属性名是:id---属性值是:2
节点名是:name---节点值是:安徒生童话
节点名是:year---节点值是:2004
节点名是:price---节点值是:77
节点名是:language---节点值是:English
======================结束遍历第2本书的内容=================
SAX解析结束
~~~~~共有2本书
冰与火之歌
乔治马丁
89
null
----finish----
安徒生童话
null
77
English
----finish----
比起SAX读取XML来,SAX写XML要相对复杂一些
SAX写
实现思路:
1.创建保存xml的结果流对象StreamResult;
2.然后利用SAXTransformerFactory这个工厂创建TransformerHandler这个操作者;
3.操作这个TransformerHandler获取Transformer,利用Transformer创建节点信息;
具体代码如下:
public void buildXml01(){
try {
//创建保存xml的结果流对象
Result reultXml = new StreamResult(new FileOutputStream(new File("c:\user.xml")));
//获取sax生产工厂对象实例
SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
//获取sax生产处理者对象实例
TransformerHandler transformerHandle = saxTransformerFactory.newTransformerHandler();
transformerHandle.setResult(reultXml);
//获取sax生产器
Transformer transformer = transformerHandle.getTransformer();
//transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");//xml的编码格式
transformer.setOutputProperty(OutputKeys.INDENT,"yes");//换行
//开始封装document文档对象,这里和解析一样都是成双成对的构造标签
transformerHandle.startDocument();
AttributesImpl attrImple = new AttributesImpl();
transformerHandle.startElement("", "", "Users",attrImple);
attrImple.addAttribute("", "", "id", "string", "123");
transformerHandle.startElement("", "", "user", attrImple);
transformerHandle.characters("这个是用户的信息".toCharArray(), 0, "这个是用户的信息".length());
transformerHandle.endElement("", "", "user");
transformerHandle.endElement("", "", "Users");
//因为没有appendChild等等添加子元素的方法,sax提供的是构造在startElement()和endElement()区间内的标签为包含的节点的父节点
transformerHandle.endDocument();
System.out.println("xml文档生成成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}