Java操作XML

1.XML基础

1.1 简介

​ xml eXtensible Markup Language 可扩展标记语言。

​ 用途:用作数据的格式化存储,常用做配置文件,和 网络数据传输

1.2 格式良好的xml

  1. 声明信息:

    <?xml version="1.0" encoding="utf-8" ?>
    
  2. 注意空格:

    • < ? 和 xml 之间不要有空格;

    • 属性值中不要加空格

    • 标签名前也不能有空格

      < book></book> <!-- book前有空格,这就是错的 -->
      
  3. 有且仅有一个根元素

  4. 大小写敏感

  5. 标签成对,真确嵌套

  6. 属性值要使用引号,单引号或双引号都行

  7. 空元素可以没有结束标签但必须闭合,如:<close/>

  8. 注释

1.3 有效的xml

  1. 格式良好
  2. 要有约束 ,常用约束有: DTD约束、 Schema约束

2. DTD约束

2.1 简介

DTD Document Type Definition 文档类型定义,用于约束xml文档的格式。

引入方式分为:内部DTD 和 外部DTD 两种。

2.2 内部DTD语法

格式:

<!DOCTYPE 根元素 [
    <!ELEMENT 根元素 (子元素*)>
    <!ELEMENT 元素 (子元素,子元素,子元素,子元素)>
    <!ATTLIST 元素 属性名 CDATA #FIXED "abc">
    <!ELEMENT name (#PCDATA)>                
]>
  1. 数量词 ? + *

  2. 属性类型

    CDATA 属性值为字符串

    ID 值为唯一的id

    ……

  3. 属性默认值

    自定义

    #REQUIRED 必须的

    #IMPLIED 不是必须的

    #FIXED value 固定值,xml中不用写,写的话必须是指定的值。【用Chrome解析时,默认值会自动加上,不知道其他解析器会不会自动加上默认值】

  4. 注意:

    1. 注意空格要有。
    2. 注意都是大写。
    3. Chrome解析时,就算xml不符合DTD也不会有任何提示。
    4. 那DTD约束在什么时候发挥作用,只能是在程序中主动校验吗?
  5. 示例

    <!-- 内部DTD -->
    <!DOCTYPE books [
        <!ELEMENT books (book*)>
        <!ELEMENT book (name,author,type,content?)>
        <!ATTLIST book id CDATA #FIXED "abc">
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT author (#PCDATA)>
        <!ELEMENT type (#PCDATA)>   
        <!ELEMENT content (#PCDATA)>
    ]>
    
  6. 外部DTD

  • 新建.dtd文件
  • 文件第一行:<?xml version=“1.0” encoding=“utf-8” ?>
  • 文件中直接写内部DTD 中括号 中的内容。
  • 在xml中引入DTD约束: <!DOCTYPE books SYSTEM “…/00/aa.dtd” >
  • 注意DTD中的名词都要大写。
  • Chrome打开xml是好像外部dtd无效【默认值无法自动补上】

3. Schema约束

3.1 简介

  1. Schema是新的XML文档约束, 比DTD强大很多,是DTD 替代者;
  2. Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。
  3. Schema 功能更强大,内置多种简单和复杂的数据类型
  4. Schema 支持命名空间 (一个XML中可以引入多个约束文档)

3.2 示例

<?xml version="1.0"?>
<xsd:schema xmlns="http://www.lagou.com/xml"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.lagou.com/xml"
            elementFormDefault="qualified">
    <xsd:element name="students" type="studentsType"/>
    <xsd:complexType name="studentsType">
        <xsd:sequence>
            <xsd:element name="student" type="studentType" minOccurs="0"
                         maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="studentType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="age" type="ageType" />
            <xsd:element name="sex" type="sexType" />
        </xsd:sequence>
        <xsd:attribute name="number" type="numberType" use="required"/>
    </xsd:complexType>
    <xsd:simpleType name="sexType">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="male"/>
            <xsd:enumeration value="female"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="ageType">
        <xsd:restriction base="xsd:integer">
            <xsd:minInclusive value="0"/>
            <xsd:maxInclusive value="200"/>
        </xsd:restriction>
    </xsd:simpleType>
    <xsd:simpleType name="numberType">
        <xsd:restriction base="xsd:string">
            <xsd:pattern value="hehe_\d{4}"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

3.3 引入

<?xml version="1.0" encoding="UTF-8" ?>
<students
        xmlns="http://www.lagou.com/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.lagou.com/xml student.xsd"
>
    <student number="hehe_1234">
        <name>张百万</name>
        <age>25</age>
        <sex>female</sex>
    </student>
    <student number="hehe_0000">
        <name>小斌</name>
        <age>20</age>
        <sex>male</sex>
    </student>
</students>

4. Java解析XML

4.1 常见解析技术比较

  1. DOM 官方提供的解析方式,基于xml树,一次性加载所有元素

  2. SAX 解析,基于事件的解析,适用于数据量大的情况,不会一次性加载

  3. JDOM,第三方工具,开源免费,比DOM快

  4. DOM4J,第三方开源免费,JDOM的升级版,是对DOM和SAX的封装

4.2 DOM4J解析

DOM4J 同时支持SAX和DOM两种解析方式。

  1. 打开文件

    创建SAXReader或DOMReader,决定了解析方式,但用法一样。

    // SAX方式 获取Document对象
    SAXReader reader = new SAXReader();
    Document doc= reader.read("H:\\tmp.xml");  
    // 注意:路径这样写,如果含有中文会报错,解决方式是,在路径前加上 file:/// 或者将路径字符串转化为File对象。 
    
    // DOM 方式获取Document对象 
    // 这种方式不能直接从文件读取,需要先手动构建 org.w3c.dom.Document
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    org.w3c.dom.Document parse = db.parse("file:///..."); // 这里的路径问题同上。
    // 换化为 org.dom4j.Document
    DOMReader reader = new DOMReader();
    Document doc = reader.read(parse);
    
  2. 文档操作

    // 首先用文档对象doc获得根元素
    Element root = doc.getRootElement();
    // XML中所有的节点都用Element表示,这里的root就是根节点
    
    // element接口定义了很多方法,以此来实现对XML的操作
    // 其中 获取和修改 节点信息 的有:
    String n = e.getName();   // 获取节点名(标签名)
    e.setName("library");	  // 修改标签名
    e.getText();			  
    e.setText(text);		  // 获取和修改标签内的文本(当标签中不再是标签时)
    // 遍历相关
    List es = e.elements();   // 获取子节点列表,没有泛型,应该是当时java1.4,还没有泛型机制
    Element e2 = e.element("节点名");
    Element p = e.getParent();	// 获取父节点
    // 增删子节点
    Element newe = e.addElement("节点名");  // 增加节点,返回新增加的节点
    e.remove(Element e);      // 删除指定子节点
    // 属性操作
    String att = e.attributeValue("属性名");  // 根据属性名,获取属性值
    Attribute a = e.attribute("attrName");   // 根据属性名,获取属性对象
    List as = e.attributes();  // 获取元素的属性列表
    e.remove(Attribute a);	   // 删除属性
    e.addAttribute("name","value");  // 添加属性
    ---
    a.getName();	// 获取属性名
    a.getValue();   // 获取属性值
    a.setValue("XXX"); 	// 设置属性值
    
  3. 写入文件

    有时候我们修改了节点的内容,若要长期存储,就要把修改后的Document重新写回文件。

    // 方式一:
    doc.write(FileWriter fw);
    // 通过Document的write方法直接写出,但是这样写出的文件没有格式内容都在一行内,不利于我们人 来阅读。
    
    // 方式二
    OutputFormat format = OutputFormat.createPrettyPrint(); // 这个方法返回的是一个格式化对象
    XMLWriter writer = new XMLWriter(fileWriter, format); // fileWriter是输出文件的字符流 
    writer.write(doc);  // 将Document写出
    writer.close();
    // 这种方式输出的格式良好,便宜阅读
    

    还有一种情况就是,我们不是修改,而是要生成一个XML文件。这时候没有 read( ) 这一步,所以我们还需要Document的创建方法。

    // 通过 DocumentHelper 创建Document对象
    Document doc = DocumentHelper.createDocument(DocumentHelper.createElement("name"));
    // 由于新创建的doc一个元素也没有,故而不可呢通过Element的addElement()方法添加节点,所以还是要通过DocumentHelper创建
    // 当然也可以像下面这样分开操作,但我更喜欢像上面这样写在一起
    Document doc = DocumentHelper.createDocument(); // 创建空doc
    Element root = DocumentHelper.createElement("name"); // 创建一个节点
    doc.setRootElement(root);
    
  4. 示例:

    @Test // 将 employee 表中数据读出,存放到 employee.xml 文件中
    public void test5() throws Exception{
        // 读取 数据到 employee 列表
        List<Map<String,Object>> el = qr.query("select * from employee", new MapListHandler());
        // 创建 xml 文件
        Document doc = DocumentHelper.createDocument(DocumentHelper.createElement("employees"));
        Element root = doc.getRootElement();
    
        // 遍历添加节点
        for(Map<String,Object> em : el){
            Element e = root.addElement("employee");
            for(String colName : em.keySet()){
                if("id".equals(colName)){
                    e.addAttribute(colName,em.get(colName).toString());
                }else {
                    Element node = e.addElement(colName);
                    node.setText(em.get(colName).toString());
                }
            }
        }
    
        // 保存到文件
        OutputFormat format = OutputFormat.createPrettyPrint();
        XMLWriter writer = new XMLWriter(new FileWriter("C:\\Users\\默尘\\Desktop\\新建文件夹\\employee.xml"),format);
        writer.write(doc);
        writer.close();
        System.out.println("over");
    }
    
    @Test // 读取 employee.xml 文件,并显示
    public void test49() throws Exception{
        // 构建 org.w3c.dom.Document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        org.w3c.dom.Document parse = db.parse("file:///C:\\Users\\默尘\\Desktop\\新建文件夹\\employee.xml");
        // 换化为 org.dom4j.Document
        DOMReader reader = new DOMReader();
        Document doc = reader.read(parse);
        // 获取root对象
        Element root = doc.getRootElement();
        List elements = root.elements();
        for(Object o : elements){
            Element e = (Element)o;
            System.out.println(e.getParent().getName());
            System.out.print(e.attributeValue("id")+"\t");
            System.out.print(e.elementText("name")+"\t");
            System.out.print(e.elementText("gender")+"\t");
            System.out.print(e.elementText("salary")+"\t");
            System.out.print(e.elementText("join_date")+"\t");
            System.out.print(e.elementText("dept_id")+"\n");
        }
    }
    

    image-20201220234019876

4.3 使用XPath定位

1. XPath简介

​ XPath 是一门在 XML 文档中查找信息的语言。 可以是使用xpath查找xml中的内容。

XPath 的好处

​ 由于DOM4J在解析XML时只能一层一层解析,所以当XML文件层数过多时使用会很不方便,结合 XPATH就可以直接获取到某个元素

2. 语法

表达式解释
/以 / 开头表示路径为绝对路径
但 / 并不表示根节点,如过XML的根节点为Books,那么 /Books 才能选中这个根节点。
//*** //XXXX 的作用是:
当前路径下搜索 所有 所有满足XXX条件的元素(不论其是不是当前路径下的直接子元素)
这里的当前路径是指 *** 指定的路径,如果没有***,也就是说以 // 开头是表示全文搜索。
XXX 一般为单个节点名,但是也可以是路径,表示找出子目录下 路径以XXX结尾的 所有元素
举例: /student//book/name
表示:先从更目录下找出所有的student节点,再以找到的节点为当前节点,查找其下的 所有book节点中的 所有name节点;这里的book节点可以不是student的直接子节点,
.表示当前节点
表示父节点
@表示选取属性
例如:/Book/@id 表示选取根目录下所有Book节点的 id 属性
[XX]中括号跟在节点之后,对匹配到的节点进行筛选。

XX可以是数字,表示要第几个【索引从1开始】,
如://Books/Book[2] 表示选取Books节点中的第二个book,但是Books可能有多个,所以,最终结果可能有多个。

XX 还可以是 @attr=xx 表示通过attr属性进行筛选,不写等于什么时,表示要求包含attr属性。

XX 还可以:
[last()-1] 表示倒数第二个
[position()❤️] 表示是前两个元素
|可以拼接多条路径

3. 在DOM4J 中使用XPath

  1. 导包

    DOM4j要是用XPath,就要依赖于 jaxen ;

    image-20201220234019876

    下载网址:jaxen-1.1.3.jar

  2. 相关方法

    // Dom4j 通过Element的以下两个方法 使用XPath
    Node selectSingleNode(String XPath); // 获取单个节点,若果XPath匹配到多个,则只返回第一个
    List selectNodes(String XPath);	// 以List返回所有匹配到的结果。
    
  3. Node与Element的关系

    Element extends Branch extends Node 
    

    Node中主要定义了一些增删方法,但一般用不到

    一般使用XPath就是为了方便的读取XML内容,而且会直接通过XPath定位到目标元素或属性,所以只需要Node的 getText() 方法获取节点或属性的内容即可。

  4. 关于相对路径

    不以 / 开头就是相对路径,相对的当前路径为 调用select方法的节点

    当以 / 或者 // 开头时,就不是相对路径,也就不受方法调用者的影响。

  5. 示例

    @Test
    public void test80() throws Exception{
        SAXReader reader = new SAXReader();
        Document doc = reader.read(new File("H:\\tmp.xml"));
        Element root = doc.getRootElement();
        // 以绝对路径搜索学号为 hehe_0000 的学生的姓名
        Node node = root.selectSingleNode("/students/student[@number='hehe_0000']/name");
        System.out.println(node.getText());
        System.out.println("-------------------------");
        // 以相对路径搜索 hehe_0002 学生拥有的所有书的书名
        List es = root.selectNodes("student[@number='hehe_0002']//Book/name");
        for(Object o : es){
            Element e = (Element)o;
            System.out.println(e.getText());
        }
    }
    

    H:\\tmp.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <students> 
      <student number="hehe_1234"> 
        <name>张百万</name>  
        <age>25</age>  
        <sex>female</sex> 
      </student>  
      <student number="hehe_0000"> 
        <name>小斌</name>  
        <age>20</age>  
        <sex>male</sex> 
      </student>  
      <student number="hehe_0001">
        <name>永强</name>
        <age>15</age>
        <sex></sex>
    	<Books>
    		<Book id="1">
    			<name>朝花夕拾</name>
    			<Author>鲁迅</Author>
    		</Book>
    		<Book id="3">
    			<name>钢铁是怎样炼成的</name>
    			<Author>那啥啥~司机</Author>
    		</Book>
    	</Books>
      </student>
      <student number="hehe_0002">
        <name>铁汉</name>
        <age>45</age>
        <sex></sex>
    	<Books>
    		<Book id="2">
    			<name>钢铁是怎样炼成的</name>
    			<Author>那啥啥~司机</Author>
    		</Book>
    		<Book id="4">
    			<name>朝花夕拾</name>
    			<Author>鲁迅</Author>
    		</Book>
    	</Books>
      </student>
    </students>
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值