零、创建一个XML文件
如果学过HTML,那么这就看着很轻松了,就是由一个一个标签组成的,开头的第一行是版本和编码的声明,一般不会改变。这里我们创建一个简单的books.xml文件。
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<name>老人与海</name>
<author>海明威</author>
</book>
<book id="2">
<name>茶花女</name>
<author>小仲马</author>
</book>
</books>
一、使用DOM解析XML节点
解析XML文件需要使用几个类,我们直接通过代码来看一下。
1、加载解析XML文件
先创建一个DocumentBuilderFactory
对象,然后通过该对象的newDocumentBuilder()
方法创建一个DocumentBuilder
对象,这里需要捕获一下异常。然后通过该对象的parse()方法解析XML文件,该方法接收一个文件路径的参数(默认项目根目录)。
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("books.xml");
}catch (Exception e){
e.printStackTrace();
}
2、获取节点属性
这里我们要获取每一个book节点,接下来的方法就js的DOM操作差不多了。获取到的所有节点对象一个是NodeList
类型。我们可以通过该对象的getLength()
方法获取到该节点的个数。
NodeList bookList = document.getElementsByTagName("book");
System.out.println(bookList.getLength()); // 2
以上获取到的是一个节点的集合,我们可以通过遍历来获取每一个节点。这里用到的是NodeList
对象的item()
方法。
for(int i = 0; i < bookList.getLength(); i++){
Node book = bookList.item(i);
}
上面的xml文件中的每个book节点都有一个id属性,我们来获取它。这里有两种常见的情况,在属性名未知的时候,可以先获取所有属性再进行遍历:
NamedNodeMap attrs = book.getAttributes();
for (int j = 0; j < attrs.getLength(); j++){
Node attr = attrs.item(j);
System.out.println(attr.getNodeName() + ":" + attr.getNodeValue());
}
getAttributes()
方法获取所有属性是一个NamedNodeMap 类型的对象,然后她也有getLength()
方法和item()
方法,由此可以获取到该节点的每一个属性,是一个Node类型的对象。最后通过该对象的对应方法获取属性名和属性值。
感觉好繁琐呀,如果我已经知道这个节点有一个id属性呢?通过节点集合的item方法获取到一个Element
类型的对象,这里需要强转一下类型。直接给该对象的getAttribute()
方法传递一个属性名参数就可以获取到其属性值。
Element book = (Element) bookList.item(i);
String attrValue = book.getAttribute("id");
System.out.println(attrValue);
3、获取节点的子节点
每一个book节点又有子节点,我们来看一下这么获取:通过节点的getChildNodes()
方法获取子节点集合对象。同样通过item()
方法获取每一个子节点。
NodeList childNodes = book.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++){
System.out.println(childNodes.item(j).getNodeName()); //打印每一个子节点名
}
这里注意,空格会被当做是Text类型的节点。打印的节点名永远都是#text。
节点名称 | NodeType | nodeName的返回值 | nodeValue的返回值 |
---|---|---|---|
Element | 1 | 节点的标签名 | null |
Attr | 2 | 属性名称 | 属性值 |
Text | 3 | #text | 节点内容 |
所以这里我们通过节点类型来判断一下,以便我们获取到真正的标签节点。
// 只要Element 类型的节点
if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
// ...
}
4、获取节点的内容
这里也有两种常见的情况,节点中文本内容前没有其他子节点的时候:通过getFirstChild()
获取到第一个节点(其实就是文本节点),然后getNodeValue()
得到文本节点的内容。这种方法算是碰巧能取到内容。
String text = childNodes.item(j).getFirstChild().getNodeValue();
如果节点中还有节点怎么,比如上面的name节点改一下:<name><a>书名:</a>老人与海</name>
。可以通过getTextContent()
方法获取节点下的所有文本内容。直接把标签节点忽略。
String text2 = childNodes.item(j).getTextContent();
System.out.println(text2); //书名:老人与海
二、SAX解析xml文件
SAX解析需要先创建一个继承于DefaultHandler
的类。通过重写方法来对XML文件进行解析。
public class SAXHandler extends DefaultHandler {
//遍历开始标签
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
}
//遍历结束标签
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
//用来标识解析开始
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("解析开始了");
}
//标识解析结束
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
// 可以用来获取节点中的文本
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
}
}
1、解析属性
我们在遍历开始标签的startElement方法里,来解析节点。参数qName就是标签名,attributes是所有的属性。
// 如果开始解析book元素
if(qName.equals("book")){
// 获取指定属性的值
String value = attributes.getValue("id");
System.out.println(value);
// 遍历属性,获取属性名和属性值
for(int i = 0; i < attributes.getLength(); i++){
System.out.println(attributes.getQName(i));
System.out.println(attributes.getValue(i));
}
}
2、解析节点的文本内容
在characters方法中,第一个参数其实是该文件的所有内容,第二个参数是在解析完一个开始标签之后的位置,第三个参数是开始标签之后到结束标签之前的长度。由此我们可结合这三个参数获取到节点的文本内容。
String text = new String(ch, start, length);
// 通过判断过滤一下空格文本节点
if (!text.trim().equals("")){
System.out.println(text); //节点中的文本
}
创建实例调用方法
然后我们在实际代码中,创建newSAXParser对象,通过的parse方法进行解析,第一个参数是文件路径,第二个参数就是一个刚才创建的SAXHandler
类的实例。
SAXParserFactory spf = SAXParserFactory.newInstance();
try {
SAXParser sp = spf.newSAXParser();
SAXHandler sh = new SAXHandler();
sp.parse("books.xml", sh);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
三、JDOM解析
JDOM并不是官方提供的方法,所以需要手动添加jar包。通过build方法解析的时候可以传入一个输入流。然后其他就不赘述了,思想都一样,直接代码一看就明白。
1、属性的解析
SAXBuilder saxBuilder = new SAXBuilder();
try {
FileInputStream fis = new FileInputStream("books.xml");
Document document = saxBuilder.build(fis);
// 获取根节点
Element rootElement = document.getRootElement();
// 获取根节点下子节点的集合
List<Element> bookList = rootElement.getChildren();
// 遍历节点
for (Element book: bookList) {
List<Attribute> attributes = book.getAttributes();
// 遍历属性
for (Attribute attr: attributes) {
System.out.println(attr.getName() + ":" + attr.getValue());
}
//通过属性名获取属性值
book.getAttributeValue("id");
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2、子节点的解析
通过getChildren获取子节点,然后通过getName()获取节点名,getValue获取节点中的文本内容。
List<Element> bookChilds = book.getChildren();
for (Element ele : bookChilds) {
System.out.println(ele.getName()+":"+ele.getValue());
}
name:老人与海
author:海明威
name:茶花女
author:小仲马
3、补充:乱码问题
如果解析碰到乱码,先修改xml文件的编码格式,如果还是达不到自己想要的效果,我们可以通过在创建输入流的时候指定编码格式。
4、存到java对象
首先创建一个book对象,get和set方法就不写了。
public class Book {
private String id;
private String author;
private String name;
private String isHot;
}
然后在遍历属性和子节点的时候,将对应的值添加到Book对象中,遍历完一次之后添加到一个List中,然后清空进行下一次遍历。
ArrayList<Book> booksList = new ArrayList<Book>();
SAXBuilder saxBuilder = new SAXBuilder();
try {
Document document = saxBuilder.build("books.xml");
// 获取根节点
Element rootElement = document.getRootElement();
// 获取根节点下子节点的集合
List<Element> bookList = rootElement.getChildren();
// 遍历节点
Book b = new Book();
for (Element book: bookList) {
List<Attribute> attributes = book.getAttributes();
for (Attribute attr : attributes) {
if (attr.getName() == "id"){
b.setId(attr.getValue());
}
if (attr.getName() == "isHot"){
b.setIsHot(b.getIsHot());
}
}
List<Element> bookChilds = book.getChildren();
for (Element ele : bookChilds) {
if (ele.getName() == "author"){
b.setAuthor(ele.getValue());
}
if(ele.getName() == "name"){
b.setName(ele.getValue());
}
}
booksList.add(b);
b = null;
}
System.out.println(booksList);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
四、DOM4J解析
同样需要手动添加包到项目。
1、属性解析
这里不同的是从根节点下获取到一个book节点的遍历器。
SAXReader reader = new SAXReader();
// 加载xml文件,获取document对象
try {
Document document = reader.read(new File("books.xml"));
// 获取根节点
Element books = document.getRootElement();
// 获取迭代器
Iterator it = books.elementIterator();
while (it.hasNext()){
Element book = (Element)it.next();
List<Attribute> bookAttrs = book.attributes();
for(Attribute attr : bookAttrs){
System.out.println(attr.getName() +":"+ attr.getValue());
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
2、解析子节点
上面从根节点获取到一个book子节点的遍历器,这里同样通过book节点的elementIterator方法获取到一个book节点下子节点的遍历器。
Iterator itt = book.elementIterator();
while (itt.hasNext()){
Element bookChild = (Element) itt.next();
System.out.println(bookChild.getName());
}
一、DOM方法
首先和解析XML文件一样需要创建一些对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document document = db.newDocument();
创建一个根节点和一个子节点
Element books = document.createElement("books");
Element book = document.createElement("book");
给节点添加属性:
book.setAttribute("id", "0");
给节点添加文本内容:
book.setTextContent("安娜卡列尼娜");
把子节点插入到根节点,并且把根节点插入到document
树:
books.appendChild(book);
document.appendChild(books);
最后把document
树写进xml文件中:
TransformerFactory tf = TransformerFactory.newInstance();
try {
Transformer transformer = tf.newTransformer();
// 把节点数写进文件中
transformer.transform(new DOMSource(document), new StreamResult(new File("myBooks.xml")));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}catch (TransformerException e) {
e.printStackTrace();
}
SAX
。。。
DOM4J
这里先了解一下RSS
,它是XML的一种典型应用。是一种描述和同步网站内容的格式。
// 创建document对象
Document document = DocumentHelper.createDocument();
// 创建根节点
Element rss = document.addElement("rss");
rss.addAttribute("version", "2.0");
XMLWriter writer = null;
try {
writer = new XMLWriter(new FileOutputStream(new File("rss.xml")));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
writer.write(document);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
此时我们已经成功创建了xml文件,
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"/>
那么接下我们继续创建子节点和内容,调用创建出来的节点对象的addElement
就是创建子节点,setText
方法是设置内容。
// 生成子节点和内容
Element channel = rss.addElement("channel");
Element title = channel.addElement("title");
title.setText("我是内容");
此时生成的文件还是没有一个很好的格式,我们来设置一下:
// 设置想xml格式(缩进、换行、编码)
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK"); // 可以设置编码,默认是UTF-8
// 在XMLWriter的构造函数中,将format 对象当做第二个参数传入
writer = new XMLWriter(new FileOutputStream(new File("rss.xml")), format);
这样我们的xml文件的格式就非常美观了
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>我是内容</title>
</channel>
</rss>
需要注意,如果有特殊字符的情况下默认会给我们进行转义的,比如
title.setText("<我是内容");
// 最后生成时这样的
<title><我是内容</title>
想要禁止自动转义也很简单,调用writer对象的setEscapeText()方法设置为false就OK了。
writer.setEscapeText(false);
//这时生成的内容就是没有转义的
<title><我是内容</title>
JDOM
先创建根节点,添加到document。
// 创建根节点并添加属性,插入到document
Element rss = new Element("rss");
rss.setAttribute("version", "2.0");
Document document = new Document(rss);
// 将document对象转换成xml文档
XMLOutputter xo = null;
try {
xo = new XMLOutputter();
} catch (Exception e) {
e.printStackTrace();
}
try {
xo.output(document, new FileOutputStream(new File("rssNew.xml")));
} catch (IOException e) {
e.printStackTrace();
}
创建子节点及子节点的文本,
Element channel = new Element("channel");
rss.addContent(channel);
Element title = new Element("title");
title.setText("我是内容");
channel.addContent(title);
设置格式,也是创建一个format对象然后传入XMLOutputter的构造函数。
Format format = Format.getCompactFormat();
format.setIndent(""); //设置换行
format.setEncoding("GBK"); //设置编码
XMLOutputter xo = new XMLOutputter(format);