<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<book>
<b>
<作者>张三</作者>
<价格>19000</价格>
</b>
<b>
<作者>李四</作者>
<价格>12.00</价格>
</b>
</book>
一、XML的三种解析方式
1. dom解析
java JDK中 提供的 解析方法, 特别的是,它可以修改 XML文件中的内容
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
@Test
public void XMLParse() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse("book.xml");
String s = d.getElementsByTagName("作者").item(0).getTextContent();
System.out.println(s);
s = d.getElementsByTagName("价格").item(0).getTextContent();
System.out.println(s);
}
@Test
public void XMLTransfer() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse("book.xml");
d.getElementsByTagName("价格").item(0).setTextContent("19000");
TransformerFactory t = TransformerFactory.newInstance();
Transformer newTransformer = t.newTransformer();
Source xmlSource = new DOMSource(d);
Result outputTarget = new StreamResult("book.xml");
newTransformer.transform(xmlSource, outputTarget);
}
2. sax解析
这里我最想说的是 里面使用到了 适配器设计模式
DefaultHandler 此类实现了 ContentHandler 等多个接口 去都是一个空的方法 实现了接口中的方法声明
这样 就是的程序员可以 定义自的handler 去继承 DefaultHandler ,只实现自己感兴趣的方法就可以了
@Test
public void SAXParse() throws Exception {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setContentHandler(new SaxHandler());
xr.parse("book.xml");
}
class SaxHandler extends DefaultHandler { //适配器设计模式
ArrayList<Book> list = null;
Book book = null;
String str = null;
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if ("作者".equals(str)) {
book.setAuthor(new String(ch, start, length));
} else if ("价格".equals(str)) {
book.setPrivce(new String(ch, start, length));
}
}
@Override
public void startDocument() throws SAXException {
list = new ArrayList<>();
}
@Override
public void endDocument() throws SAXException {
System.out.println(list);
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if ("作者".equals(qName)) {
str = new String("作者");
} else if ("价格".equals(qName)) {
str = new String("价格");
} else if ("b".equals(qName)) {
book = new Book();
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if ("作者".equals(qName)) {
str = null;
} else if ("价格".equals(qName)) {
str = null;
} else if ("b".equals(qName)) {
list.add(book);
book = null;
}
}
}
3. xmlpull解析 需要自己导入xmlpull 的 jar包
Android 中经常使用它来 解析 XML
@Test
public void PullXMLParser() throws Exception {
XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();
XmlPullParser xpp = xppf.newPullParser();
xpp.setInput(new FileInputStream("book.xml"), "utf-8");
ArrayList<Book> list = null;
Book book = null;
int type = 0;
while ((type = xpp.getEventType()) != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_DOCUMENT:
list = new ArrayList<>();
break;
case XmlPullParser.START_TAG:
book = new Book();
if ("作者".equals(xpp.getName())) {
book.setAuthor(xpp.nextText());
} else if ("价格".equals(xpp.getName())) {
book.setPrivce(xpp.nextText());
}
break;
case XmlPullParser.END_TAG:
if ("b".equals(xpp.getName())) {
list.add(book);
book = null;
}
break;
}
xpp.next();
}
System.out.println(list);
}
三、 自己使用 数据结构 栈 来实现对 XML 文件的简单解析工作
以上述XML代码为例,使用栈结构来完成对XML的解析
<book>
<b>
<作者>
张三</作者>
<价格>
19000
</价格>
</b>
<b>
<作者>
李四</作者>
<价格>
12.00
</价格>
</b>
</book>
为了突出终点我将每一个标签保存为一行 使用正确格式的XML 文档
@Test
public void MyXMLParse() throws Exception {
Stack<String> stack = new Stack<>();
BufferedReader br = new BufferedReader(new FileReader("book.xml"));
String str = null;
while ((str = br.readLine()) != null) {
if(isEndTag(str) == false) { //不是一个结束标签
stack.add(str);
} else { //是一个结束标签
while(true) {
if(ToBeginTag(str).equals(stack.peek()) == true) {
stack.pop();
break;
}else {
System.out.println(stack.pop());
}
}
}
}
System.out.println(stack.size() == 0);
br.close();
}
public boolean isText(String str) {
if(str.substring(0,1).equals("<")) {
return false;
}
return true;
}
public boolean isEndTag(String str) {
if ("/".equals(str.substring(1, 2))) {
return true;
}
return false;
}
public String ToBeginTag(String str) {
if (isEndTag(str))
return str.substring(0, 1) + str.substring(2);
else
return str;
}
解释原理
使用建模的思想 可以把 XML转换为
(((张三)(19000))((李四)(12.00)))
从左到右遍历字符串
如果遇到 ‘(’左括号和汉字 那么 就进栈
如果 遇到 ‘)’右括号 那么 就弹栈 知道 弹出一个 ‘(’ 就停止。
这样遍历完之后, 如果栈为空那么就说明 括号是匹配的
情景分析
在遍历前三个括号的时候 它们都是左括号 栈情况为 ------> '(' '(' '('
遇到 ‘张三’ 不是右括号 就进栈 栈情况为 -----> '(' '(' '(' '张三'
继续遍历遇到 ‘)’ 那么弹出 ‘张三’并打印 然后弹出 ‘(’ 停止弹栈 栈情况为 ------> '(' '('
遇到 ‘(’ 和 ‘19000’ 都会进栈 栈情况为 ------> '(' '(' '(' '19000'
遇到 ‘)’ 那么弹出 ‘19000’并打印 然后弹出 ‘(’ 停止弹栈 栈情况为 ------> '(' '('
遇到 ‘)’ 那么弹出 ‘(’ 栈情况为 ------> '('
...... 最后结果 栈为空 表示 表达式中的括号是正确匹配的