Java解析XML

《java世界观》文集说明

  • 本文集记载的所有内容均是java的学习笔记,主要重点记录概念,可能不会为每个概念列举完整的代码例子,更多代码例子请移步《java种田记》文集
  • 本文主要涉及内容来自开课吧新职课-JavaEE开发工程师V2.0
  • 若有错漏之处,欢迎各位指正

XML简介

XML,可扩展标记语言(eXtensible Markup Language),主要用于网络数据传输,数据存储,及配置文件等。
这里简单介绍一下XML的基础语法:

  • XML文档声明 <?xml version="1.0" encoding="UTF-8"?>
  • XML文档由一对对标记组成
  • 属性可以在开始标记中被描述,属性以 键="值" 的方式存储,多个属性之间由空格分割
  • XML文档中必须有且只有一个根标记
  • 标记可以嵌套,但不允许交叉

以下是一个例子

<?xml version="1.0" encoding="UTF-8"?>
<drinks>
    <drink id="1">
        <name>寒天爱玉</name>
        <ice>少冰</ice>
        <sugar>少糖</sugar>
        <addons>
            <addon>奶盖</addon>
            <addon>椰果</addon>        
        </addons>
    </drink>
    <drink id="2">
        <name>奶盖乌龙</name>
        <ice>去冰</ice>
        <sugar>七分糖</sugar>
        <addons>
            <addon>珍珠</addon>
            <addon>椰果</addon>        
        </addons>
    </drink>
</drinks>

XML的解析方式

SAX解析

SAX, Simple API for XML

  • 事件驱动的解析机制
  • SAX解析器会逐行阅读,当读取到一个标记的开始、属性、内容、结束时触发事件
  • 需要编写事件触发的相应处理程序
优点
  • 无需等待数据全部加载完毕,立即开始分析
  • 逐行加载,节省内存
  • 不必解析整个文档,可以在满足某个条件时停止解析
缺点
  • 单向解析,无法对之前的数据再进行操作
  • 逐行解析无法得知复杂的元素的层次,只能维护父子关系的节点
  • 只读解析,无法修改内容

DOM解析

DOM,文档对象模型(Document Object Model),是w3c官方制定的用以处理XML文档的标准编程接口。DOM将XML文档解析成树状结构,程序员可以通过操作此文档树来获取、修改、删除数据。

优点
  • 文档被加载至内存,允许对数据和结构进行更改
  • 支持双向解析数据
缺点
  • 文档全部被加载至内存中,消耗资源大

Java中的DOM解析

对比SAX解析和DOM解析,后者的缺点可以忽略不计,因此主要使用DOM解析的方式处理XML文档

JDOM

如上文所说,DOM解析本身是面向所有平台的接口,DOM提供给Java的接口也只是达到了能够实现的最低通用标准,并没有特地考虑到Java的特性并作出相应的优化设计,因此Java程序员直接使用DOM的接口则会感觉到不便。
JDOM则是为了解决这一问题,由Brett McLaughlin和Jason Hunter开发出来的开源项目,该项目致力于提供完善方便的用Java解析XML的解决方案。

优点
  • 实现了许多工具类,比起直接使用DOM的API,简便了XML的解析过程
  • 加入了大量java集合,方便java程序员进行开发
缺点
  • 不是面向接口设计,没有较好的灵活性
  • 优化并不是很好,性能没有那么优异

DOM4J

DOM4J也是为Java设计的用于解析XML的开源API(Application Programming Interface,应用程序接口)
和JDOM相比,DOM4J的性能更为优异,功能更加强大,使用更加便捷,因此也更为常用

代码实现步骤
  1. 下载并引入dom4j.jar
  2. 创建XML读取工具对象 SAXReader sr = new SAXReader();
  3. 通过XML读取工具对象读取文档对象(加载到内存的整个XML文档) Document doc = sr.read(<文档对象>);,这里read(<文档对象>)方法可以接收输入流,也可以直接接收文件或URL
  4. 通过文档对象获得根元素对象再进行后续操作 Element root = doc.getRootElement();
元素对象 Element 常用操作
  • 获取节点名称 String getName();
  • 获取节点内容 String getText();
  • 设置节点内容 void setText(String text);
  • 根据子节点的名称,获取匹配到的第一个子节点对象 Element element(String name)
  • 获取所有的子节点对象 List<Element> elements();
  • 获取节点的属性值 String attributeValue(String attributeName);
  • 获取子节点的内容 String elementText(String childName);
  • 添加子节点 Element addElement(String s)
  • 添加属性 Element addAttribute(String name, String value);
示例
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.List;

/**
 * @author 蜗先生
 * @date 2020/9/22
 */
public class Main {
    public static void main(String[] args) throws DocumentException {
        // 创建XML读取工具对象,并获取文档对象,文档内容在文章开头
        Document doc = new SAXReader().read(new File("drinks.xml"));
        // 获取根元素
        Element root = doc.getRootElement();
        
        // 解析示例
        // 打印根元素名称
        System.out.println(root.getName());
        // 打印所有元素信息
        List<Element> elements = root.elements();
        for (Element drink : elements) {
            System.out.println(drink.attributeValue("id"));
            System.out.println(drink.elementText("name"));
            System.out.println(drink.elementText("ice"));
            System.out.println(drink.elementText("sugar"));
            List<Element> addons = drink.element("addons").elements();
            for (Element addon : addons) {
                System.out.println(addon.getText());
            }
        }
    }
}

运行结果

drinks
1
寒天爱玉
少冰
少糖
奶盖
椰果
2
奶盖乌龙
去冰
七分糖
珍珠
椰果

Process finished with exit code 0
XPATH 路径表达式

XPath是一种用来确定XML文档中某部分位置的语言,它可以便捷的查询元素,dom4j中也提供了对XPath的支持

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从当前节点选取所有后代节点。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

@ 属性使用方式:

  • [@attributeName='value']
  • [@attributeName>'value']
  • [@attributeName<'value']
  • [@attributeName!='value']

DOM4J中的XPATH使用方式:

  • 需要导入jaxen.jar
  • Element element = (Element) document.selectSignleNode(<XPath路径表达式>);Node node = document.selectSignleNode(<XPath路径表达式>);
  • List<Element> elements = document.selectNodes(<XPath路径表达式>);

注:
Node selectSingleNode(String var1);List selecNodes(String var1);
均为接口org.dom4j.Node的方法,而org.dom4j.Documentorg.dom4j.Element均继承了此接口(当然,此接口被许多其他类实现),此接口内的方法还包括前文提到的void setText(String var1)String getText()
更多内容建议查阅API文档(github来源 第三方来源)或直接阅读源码

代码示例

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.List;

/**
 * @author 蜗先生
 * @date 2020/9/22
 */
public class Main {
    public static void main(String[] args) throws DocumentException {
        // 创建XML读取工具对象,并获取文档对象,文档内容在文章开头
        Document doc = new SAXReader().read(new File("drinks.xml"));
        
        // XPath解析示例
        // 查找所有addon元素并打印信息
        List<Element> addons = doc.selectNodes("//addon");
        for (Element addon : addons) {
            System.out.println(addon.getText());
        }

        System.out.println("--------------------");
        
        // 查找id为2的饮料的addon元素并打印信息
        addons = doc.selectNodes("//drink[@id='2']//addon");
        for (Element addon : addons) {
            System.out.println(addon.getText());
        }
    }
}

运行结果

奶盖
椰果
珍珠
椰果
--------------------
珍珠
椰果

Process finished with exit code 0
生成/修改XML
  1. 获取现有文档对象(见上文),或创建空文档对象 Document doc = DocumentHelper.createDocument();
  2. 添加或修改节点或内容(见上文)
  3. 创建储存新文档的文件输出流 FileOutputStream fos = new FileOutputStream(<filePath>);
  4. 将文件输出流转换成XML文档输出流 XMLWrite xw = new XMLWriter(fos);
  5. 写出文档 xw.write(doc);
  6. 释放资源 xw.close(); fos.close();

代码示例

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @author 蜗先生
 * @date 2020/9/22
 */
public class Main {
    public static void main(String[] args) throws DocumentException, IOException {
        // 创建XML读取工具对象,并获取文档对象,文档内容在文章开头
        Document doc = new SAXReader().read(new File("drinks.xml"));
        
        // 修改文档
        // 给id为1的饮料添加addon珍珠
        Element element = (Element) doc.selectSingleNode("//drink[@id=1]//addons");
        element.addElement("addon").setText("珍珠");
        
        // 添加id为3的饮料及相应信息
        element = doc.getRootElement();
        Element drink = element.addElement("drink");
        drink.addAttribute("id", "3");
        drink.addElement("name").setText("红枣桂圆");
        drink.addElement("ice").setText("热饮");
        drink.addElement("sugar").setText("正常");
        Element addons = drink.addElement("addons");
        // 以下配料纯属举例,请不要轻易尝试黑暗料理
        addons.addElement("addon").setText("椰果");
        addons.addElement("addon").setText("小芋圆");
        
        // 修改完毕,写出文档并关闭资源
        FileWriter fileWriter = new FileWriter("drinks2.xml");
        XMLWriter xmlWriter =  new XMLWriter(fileWriter);
        
        xmlWriter.write(doc);

        xmlWriter.close();
        fileWriter.close();
    }
}

生成的drinks2.xml文件内容
*由于直接生成的内容挤在一行不便阅读,以下内容进行过美化格式处理

<?xml version="1.0" encoding="UTF-8"?>
<drinks>
    <drink id="1">
        <name>寒天爱玉</name>
        <ice>少冰</ice>
        <sugar>少糖</sugar>
        <addons>
            <addon>奶盖</addon>
            <addon>椰果</addon>
            <addon>珍珠</addon>
        </addons>
    </drink>
    <drink id="2">
        <name>奶盖乌龙</name>
        <ice>去冰</ice>
        <sugar>七分糖</sugar>
        <addons>
            <addon>珍珠</addon>
            <addon>椰果</addon>
        </addons>
    </drink>
    <drink id="3">
        <name>红枣桂圆</name>
        <ice>热饮</ice>
        <sugar>正常</sugar>
        <addons>
            <addon>椰果</addon>
            <addon>小芋圆</addon>
        </addons>
    </drink>
</drinks>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。