dom4j 是一套开源的 XML 解析工具,它更为简单易用。
dom4j是一个“第三方工具”,其类与其方法并不在JDK自带的包中。需要导入,并添加到Class Path 中才能使用这些类和方法。
dom4j中有大量的类和w3c中的类是同名的,所以在导入包的时候注意不要导入错了。
解析 XML 文件
通过 SAXReader 对象及其方法read(),在方法的参数中传入代表XML文件的File对象,即可获得代表XML文件内容的 Document 对象。
SAXReader reader = new SAXReader();
File xmlFile = new File("Hello.xml");
Document doc = reader.read(xmlFile);
获得根元素
调用 Document 对象的 getRootElement() 方法,即可获得该XML文档的唯一的根元素对象。
Element root = doc.getRootElement();
判断Node对象的真实类型
节点是元素节点、文本节点 和 属性节点的上层概念,即,Node 类是Element 类、Text 类 和 Attribute 类的父类。
有很多方法返回的是 Node 的引用,而非具体的其子类的引用,这是多态的表现形式之一。所以有时候需要去判断一个Node的引用所指向的对象的真实类型。
有两种方法可以用于判断一个Node对象的真实类型:
// 通过Node的实例方法getNodeType(),以及相关常量
node.getNodeType() == Node.ELEMENT_NODE
node.getNodeType() == Node.TEXT_NODE
node.getNodeType() == Node.ATTRIBUTE_NODE
// 通过 instanceof 运算符
node instanceof Element
node instanceof Text
node instanceof Attribute
遍历节点的一级节点
一共有三种办法可以来遍历一个节点的子节点。
- 利用 Node 的 elements() 方法。
- 利用 Node 的 nodeCount() 方法和 node() 方法。
- 利用 Node 的迭代器访问。
注意:一个节点可以有属性节点,但属性节点在层次关系上并不是这个节点的子节点。如要获得一个节点的属性节点,是使用另外的办法。
Node 的 elements() 方法会返回一个包含该节点所有一级元素子节点的列表。
List list = root.elements();
for (Element el : list) {
System.out.println(el.getName());
}
注意,正如其名字,该方法返回的列表中仅包含该节点的元素子节点,如果这个节点中包含有文本子节点,那么文本子节点并不在返回的列表中。
Node 对象的 nodeCount() 方法会返回该节点所拥有的一级子节点的数量;node() 方法会返回参数指定索引位置的子节点。
for (int i = 0; i < root.nodeCount(); i++)
{
Node n = root.node(i);
if (n instanceof Element)
System.out.println(((Element)n).getName());
else if (n instanceof Text)
System.out.println(((Text)n).getText());
else
System.out.println("其他");
}
这种访问节点的方法可以获得该节点的所有子节点,包括元素节点和文本节点。再次提醒,不包括属性节点,因为该节点的属性节点,关系上并不是它的“子节点”。
类似于集合框架里的迭代器,Node也提供了一种类似的方式来访问节点的子节点。
迭代访问子节点的好处在于,它可以通过返回不同的迭代来控制:只访问该节点的一级元素子节点,还是访问该节点的所有一级子节点。
// 获得一个节点对象的元素迭代器,后续使用它来访问该节点的元素子节点
Iterator it = root.elementIterator();
while (it.hasNext())
{
Element el = it.next();
System.out.println(el.getName());
}
// 与上面类似,但是获得的是节点迭代器,所以可以访问该节点的所有类型的子节点。
Iterator it3 = root.nodeIterator();
while (it3.hasNext())
{
Node n = it3.next();
if (n instanceof Element)
System.out.println(n.getName());
else if (n instanceof Text)
System.out.println(n.getText());
else
System.out.println("其他");
}
Node有个重载的 elementIterator() 方法可以传入一级子元素的标签名,该方法返回的迭代器专门(仅仅)访问当前节点中指定标签名的一级子元素节点。
Iterator it4 = root.elementIterator("author");
while (it4.hasNext()) {
Element el = it4.next();
System.out.println(el.getStringValue());
}
访问一个元素节点的属性节点
一个元素节点可以有属性节点(其他节点,例如文本节点,是没有属性的),但是属性节点并非它的子节点,所以之前的代码无法访问(获得)到元素节点的属性节点。
有两种办法可以获得一个元素节点的属性节点:
- 利用 Element 的 attributes() 方法。
- 利用 Element 的属性迭代器访问。
类似于Node的elements()方法,Element 的 attributes() 方法会返回一个链表,其中当前节点的所有属性节点。
List list = el.attributes();
for (Attribute attr : list) {
System.out.println(attr.getName() + ": " + attr.getValue());
}
类似于用迭代器访问节点的子节点,Element 对象的 attributeIterator() 方法会返回一个专门用于方位该元素节点的属性节点的迭代器。
Iterator it5 = el.attributeIterator();
while (it5.hasNext()) {
Attribute attr = it5.next();
System.out.println(attr.getName() + ": " + attr.getValue());
}
创建一个新的XML文档
调用 DocumentHelper 类的静态方法 createDocument() 可以返回一个Document 对象,该对象代表着一颗新的 DOM 树。
Document 对象 和 Element 对象的 addElement() 方法可以向DOM树中添加参数指定标签名的新的元素节点,并形成层次关系。addElement() 方法会返回这个新元素节点。
Element 对象的 addAttribute() 方法可以向元素节点中添加属性,addText() 方法可以向元素节点中添加文本内容。
Document newDoc = DocumentHelper.createDocument();
Element root = newDoc.addElement("root");
Element author1 = root.addElement("author");
author1.addAttribute("name", "James");
author1.addAttribute("location", "UK");
author1.addText("James Strachan")
dom4j的作者通过“编码技巧”提供了一种新增元素节点的“简便”写法,执行效果与以上效果等价:
Element author2 = root.addElement( "author" )
.addAttribute( "name", "Bob" )
.addAttribute( "location", "US" )
.addText( "Bob McWhirter" );
只需要为 Document 对象的 writer() 方法传入一个 FileWriter 对象,就可以将 Document 对象所代表的整棵DOM树写入指定的XML文件中。
FileWriter fw = new FileWriter("author.xml");
newDoc.write(fw);
fw.close(); // 切记要关闭输出流。
Document对象 转换成 / 转换自 字符串
// Document 对象转字符串
String str = newDoc.asXML();
System.out.println(str);
// 字符串转 Document 对象
String text = "James ";
Document document = DocumentHelper.parseText(text);
FileWriter fw = new FileWriter("author.xml");
document.write(fw);
fw.close();