[学习笔记]XML解析之DOM4J

DOM4J

XML解析

1. 常用API

  • 解析器:SAXReader
  • 文档对象:Document
  • 元素对象:Element
  • 文档帮助类(用于创建节点):DocumentHelper
  • 文档对象子类:DefaultElement
  • 文档对象子类:BaseElement
  • 格式化输出对象:OutputFormat
  • 保存文件对象:XMLWriter

2. 解析XML获取文档对象

   
   
public static void main ( String [] args ) {
// 创建SAXReader对象
SAXReader reader = new SAXReader ();
// 解析XML获取Document对象
try {
Document doc = reader . read ( "/bin/LocalList.xml" );
} catch ( DocumentException e ) {
e . printStackTrace ();
}
}

3. 获取节点文本内容

   
   
private static String getElementText ( Document doc ) {
// 获取根节点
Element root = doc . getRootElement ();
// 获取指定节点集合
List < Element > elements = root . elements ( "City" );
// 获取指定节点
Element e = elements . get ( 7 );
// 获取指定节点的文本内容
String text = e . getText ();
return text ;
}

4. 递归获取所有节点的文本

   
   
private static void getAllElementText(Document doc) {
// 获取根节点
Element root = doc.getRootElement();
// 递归遍历所有节点
recursion(root);
}
 
// 使用深度优先搜索递归遍历所有节点
private static void recursion(Element node) {
// 如果非元素节点,则直接返回
if (node.getNodeType() != Element.ELEMENT_NODE) {
return;
}
// 如果该节点无子元素节点,则输出元素文本
if (node.elements().isEmpty()) {
System.out.println(node.getText());
} else {
// 如果该节点有子元素,则递归遍历
List<Element> elements = node.elements();
for (Element element : elements) {
// 递归调用
recursion(element);
}
}
}

5. 使用迭代器获取所有City节点的元素内容

   
   
private static void getCityElementText(Document doc) {
// 获取根元素节点
Element root = doc.getRootElement();
// 使用元素迭代器进行迭代
for (Iterator<Element> iterator = root.elementIterator(); iterator.hasNext();) {
Element e = iterator.next();
// 如果该节点无子元素节点,则输出元素文本内容
if (e.elements().isEmpty()) {
System.out.println(e.getText());
}
}
}

6. 将Document保存到XML中

   
   
private static void saveXML(Document doc) {
try {
// 创建XML文件的字节输出流
OutputStream out = new FileOutputStream("src/LocalList.xml");
// 获取数据格式的对象,createCompactFormat紧凑格式,createPrettyPrint缩进格式
OutputFormat format = OutputFormat.createPrettyPrint();
// 创建XMLWriter对象
XMLWriter writer = new XMLWriter(out, format);
// 将DOM树写入XML文件
writer.write(doc);
// 关闭资源
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}

7. 修改元素文本内容

   
   
private static void update(Document doc) {
// 获取要修改节点
Element root = doc.getRootElement();
Element element = (Element) root.elements().get(3);
// 修改文本内容
element.setText("天上人间");
 
// 保存文件
saveXML(doc);
}

8. 添加子元素节点

   
   
private static void addChild(Document doc) {
// 获取要添加的父元素节点
Element root = doc.getRootElement();
Element city = (Element) root.elements().get(3);
// 创建要添加的子元素节点
Element street = DocumentHelper.createElement("street");
// Element streetE1 = new DefaultElement("street");
// Element streetE2 = new BaseElement("street");
 
// 设置节点的文本内容
street.setText("南天门大街");
// 将子节点添加到父节点中
city.add(street);
 
// 保存文件
saveXML(doc);
}

9. 添加兄弟元素节点

   
   
public static void addSibling(Document doc) {
// 获取目标元素节点
Element root = doc.getRootElement();
List<Element> elist = root.elements("City");
// 创建要添加的元素节点
Element city = new DefaultElement("City");// <City></City>
// 设置新节点的文本
city.setText("哈哈区");
 
// 在指定位置加入元素 city
elist.add(4, city);
 
// 保存文件
saveXML(doc);
}

10. 删除指定节点

   
   
public static void removeElement(Document doc) {
// 获取要删除节点
Element root = doc.getRootElement();
Element city = (Element) root.elements("City").get(3);
Element street = (Element) city.elements("street").get(0);
// 用父节点删除子节点
city.remove(street);
 
// 保存文件
saveXML(doc);
}

11. 为节点添加属性

   
   
public static void addAttribute(Document doc) {
// 获取要添加属性的节点
Element root = doc.getRootElement();
Element city = (Element) root.elements("City").get(3);
// 为节点添加属性
city.addAttribute("type", "food");
 
// 保存文件
saveXML(doc);
}

XPath

概述

XPath的作用就像在一个文件系统中定位文件一样在XML文件中定位元素,XPath是由W3C的XPath 1.0标准所描述。

标准

1. 如果路径以斜线 / 开始,那么该路径就表示到一个元素的绝对路径。

/AAA/CCC:选择AAA的所有CCC子元素。

2. 如果路径以双斜线 // 开头, 则表示选择文档中所有满足双斜线//之后规则的元素(无论层级关系)。

//DDD/BBB:选择父元素是DDD的 所有 BBB元素。

3. 星号 * 表示选择所有由星号之前的路径所定位的元素。

/*/*/*/BBB:选择所有的有3个祖先元素的BBB元素。
//*:选择所有元素。

4. 方块号里的表达式可以进一步的指定元素,其中数字表示元素在选择集里的位置,而last()函数则表示选择集中的最后一个元素。

/AAA/BBB[1]:选择AAA的第一个BBB子元素。
/AAA/BBB[last()]:选择AAA的最后一个BBB子元素。

5. 使用 @ 符号来选择特定的属性或特定属性的元素。

//@id:选择所有的id属性。
//BBB[@id]:选择有id属性的BBB元素。
//BBB[not(@*)]:选择没有属性的BBB元素。

6. 属性的值可以被用来作为选择的准则,normalize-space函数删除了前部和尾部的空格,并且把连续的空格串替换为一个单一的空格。

//BBB[@id='b1']:选择含有属性id且其值为'b1'的BBB元素。
//BBB[normalize-space(@name)='bbb']:选择含有属性name且其值(在用normalize-space函数去掉前后空格后)为'bbb'的BBB元素。

7. 多个路径可以用分隔符 | 合并在一起。

/AAA/EEE | //BBB:选择所有的BBB元素和作为AAA子元素的 所有 EEE元素。

8. 相关函数

  • count():计数所选元素的个数。
  • name():返回元素的名称。
  • start-with():在该函数的第一个参数字符串是以第二个参数字符开始的情况返回true。
  • contains():当其第一个字符串参数包含有第二个字符串参数时返回true。
  • string-length:返回字符串的字符数,应用 &lt; 替代 < ,用 &gt; 代替 > 。
//*[count(BBB)=2]:选择含有2个BBB子元素的元素。
//*[name()='BBB']:选择所有名称为BBB的元素,等价于//BBB。
//*[starts-with(name(),'B')]:选择所有名称以"B"起始的元素。
//*[contains(name(),'C')]:选择所有名称包含"C"的元素。
//*[string-length(name()) < 3]:选择名字长度小于3的元素。

DOM4J中XPath的使用

1. 使用XPath获取指定节点

   
   
private static void getNode(Document doc) {
Node node = doc.selectSingleNode("/State/City/street");
System.out.println(node.getText());
}

2. 使用XPath获取节点集合

   
   
private static void getNodes(Document doc) {
List<Node> nodes = doc.selectNodes("/State/City");
System.out.println(nodes.size());
}

3. 使用XPath获取指定属性

   
   
private static void getAttribute(Document doc) {
// 获取拥有Name属性的节点
Node node = doc.selectSingleNode("//*[@Name]");
// 获取该节点的Name属性值
String name = node.valueOf("@Name");
System.out.println(name);
}

案例

学生信息存储
Dom4jUtils:DOM4J解析的工具类,包括获取文档对象方法和保存文件方法。
   
   
public class Dom4jUtils {
 
/**
* 获取XML数据的Document对象
*
* @return
*/
public static Document getDocument(String path) {
if (path == null) {
return null;
}
// 创建XML解析器
Document doc = null;
SAXReader reader = new SAXReader();
try {
// 解析XML文件
doc = reader.read(path);
} catch (DocumentException e) {
// 异常处理
System.out.println("读取XML失败!");
e.printStackTrace();
}
return doc;
}
 
/**
* 将Document对象保存到XML文件中
*
* @param doc 要保存的Document对象
* @return
*/
public static boolean saveXml(Document doc, String path) {
if (path == null || doc == null) {
return false;
}
// 创建保存格式
OutputFormat of = OutputFormat.createPrettyPrint();
// 创建XML写入对象
XMLWriter writer = null;
try {
// 将Document对象写入到XML文件中
OutputStream os = new FileOutputStream(new File(path));
writer = new XMLWriter(os, of);
writer.write(doc);
} catch (IOException e) {
// 异常处理
System.out.println("文件写入错误!");
e.printStackTrace();
return false;
} finally {
// 资源释放
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
System.out.println("资源关闭错误!");
e.printStackTrace();
return false;
}
}
}
return true;
}
}

config.xml:配置具体Dao的实现类名,xml数据存储路径。
   
   
<?xml version="1.0" encoding="UTF-8"?>
 
<dao>
<student>
<class>com.xml.dao.impl.StudentDaoByDom4j</class>
<db>src/com/xml/db/student.xml</db>
</student>
</dao>

student.xml:学生数据存储的xml文件。
   
   
<?xml version="1.0" encoding="UTF-8"?>
 
<students>
<student studentid="1255" classid="55">
<name>帅哥</name>
<gender></gender>
<score>90</score>
</student>
</students>

StudentDao:DAO的接口设计
   
   
package com.xml.dao;
 
import com.xml.bean.Student;
 
public interface StudentDao {
/**
* 添加学生信息
* @param s 要添加的学生对象
* @return
*/
public boolean add(Student s);
/**
* 删除学生的信息
* @param sid 要删除的学生id
* @return
*/
public boolean delete(int sid);
/**
* 查询学生成绩
* @param sid 要查询的学生id
* @return
*/
public float getScore(int sid);
}

StudentDaoFactory:DAO的获取工厂类,该类为枚举单例,通过config.xml的配置信息,提供获取具体DAO对象的公有方法。
   
   
package com.xml.dao;
 
import org.dom4j.Document;
 
import com.xml.util.Dom4jUtils;
 
public enum StudentDaoFactory {
dao;
 
public StudentDao getInstance() {
StudentDao sdao = null;
// 获取配置信息XML文档的Document对象
Document doc = Dom4jUtils.getDocument("bin/com/xml/config/config.xml");
// 获取StudentDao的实现类名
String className = doc.selectSingleNode("/dao/student/class").getText();
// 创建StudentDao实现类
try {
sdao = (StudentDao) Class.forName(className).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
System.out.println("配置信息错误!找不到该类。");
e.printStackTrace();
}
return sdao;
}
}

StudentDaoByDom4j:DAO的实现类,根据配置信息能够获取具体数据XML的位置,并对之操作。
   
   
package com.xml.dao.impl;
 
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
 
import com.xml.bean.Student;
import com.xml.dao.StudentDao;
import com.xml.util.Dom4jUtils;
 
public class StudentDaoByDom4j implements StudentDao {
 
private Document doc = null;
private String path = null;
 
public StudentDaoByDom4j() {
// 获取配置信息XML文档的Document对象
Document config = Dom4jUtils.getDocument("bin/com/xml/config/config.xml");
// 获取学生信息存储路径
this.path = config.selectSingleNode("/dao/student/db").getText();
// 获取学生信息XML文档的Document对象
this.doc = Dom4jUtils.getDocument(this.path);
}
 
@Override
public boolean add(Student s) {
// 性别预处理
String[] mask = {"男", "女", "人妖"};
// 在根节点下创建学生元素并设置属性
Element e = doc.getRootElement().addElement("student")
.addAttribute("studentid", String.valueOf(s.getId()))
.addAttribute("classid", String.valueOf(s.getClassId()));
// 为学生元素添加子元素
e.addElement("name").setText(s.getName());
e.addElement("gender").setText(mask[s.getGender()]);
e.addElement("score").setText(String.valueOf(s.getScore()));
// 将Document保存到XML文件中
Dom4jUtils.saveXml(doc, this.path);
return true;
}
 
@Override
public boolean delete(int sid) {
// 获取要删除的节点
Node node = doc.selectSingleNode("/students/student[@studentid='" + String.valueOf(sid) + "']");
// 使用父节点删除该节点
node.getParent().remove(node);
// 将Document保存到XML文件中
Dom4jUtils.saveXml(doc, this.path);
return true;
}
 
@Override
public float getScore(int sid) {
// 获取要查询成绩的学生成绩
String score = doc.selectSingleNode("/students/student[@studentid='"
+ String.valueOf(sid) + "']/score").getText();
// 返回成绩
return Float.parseFloat(score);
}
 
}

测试
   
   
package com.xml.test;
 
import org.junit.Test;
 
import com.xml.bean.Student;
import com.xml.dao.StudentDao;
import com.xml.dao.StudentDaoFactory;
 
public class StudentDaoTest {
private StudentDao sdao = null;
{
sdao = StudentDaoFactory.dao.getInstance();
}
 
@Test
public void testAdd() {
Student s = new Student(1248, "苏苏", 55, 2, 80);
sdao.add(s);
}
 
@Test
public void testDelete() {
sdao.delete(1248);
}
 
@Test
public void testGetScore() {
float score = sdao.getScore(1255);
System.out.println(score);
}
 
}





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值