XML基础学习

281 阅读15分钟

XML(Extensible Markup Language),扩展性标识语言。文件的后缀名为.xml。

XML的作用是传输和存储数据:

  • 可作为一种简单的数据库,存储并检索数据;
  • 传输约定格式的文件;
  • 可作为软件的配置文件。

一、XML入门

声明语法

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<person id="1001" year="2015">
    <!-- xml的注释 -->
    <nam>张三</nam>
    <age>20</age>
</person>

属性:
version   xml的版本,1.0(使用)、1.1
encoding   xml编码,gbk、utf-8、iso8859-1(不包含中文)
standalone   是否需要依赖其他文件,yes/no

注意标签要区分大小写

标签上可以有属性(多个),但是属性名不可相同

需要显示“<”或者“>”时,需要进行转义: <使用 < >使用 > &使用& ...等等

如果不想转义,可以把内容放入CDATA区,会把特殊字符当作文本内容展示:

<![CDATA[ 内容 ]]>

例如:<![CDATA[ <b>if(a<b && b<c && d>f) {}</b> ]]>

PI指令(可在xml中设置样式,仅对英文标签有效)

语法:

<?xml-stylesheet type=”text/css” href=”css的路径”?>

例子:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="1.css"?>
<person>
    <name>张三</name>
    <age>20</age>
</person>

1.css文件定义:
name { background-color:red; }
age { background-color:orange;}

约束的技术:

dtd的约束schema的约束


二、文档类型定义(DTD)

快速了解

文件后缀: .dtd

步骤:

  1. 看xml中有多少个元素, 有几个元素,在dtd文件中写几个
  2. 判断元素是简单元素还是复杂元素。
    • 复杂元素:有子元素的元素: < !ELEMENT 元素名称(子元素)>
    • 简单元素:没有子元素: <! ELEMENT 元素名称 (#PCDATA)>
  3. 需要在xml文件中引入dtd文件
    • < !DOCTYPE 根元素名称 SYSTEM "dtd文件的路径">

例子:

<!ELEMENT person (name,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
    <name>张三</name>
    <age>20</age>
    <a></a> <!--这里就会报错-->
</person>

引入方式

1、 引入外部的 dtd 文件

< !DOCTYPE  根元素名称  SYSTEM  "dtd文件的路径"> 

2、 使用内部的 dtd 文件

< !DOCTYPE 根元素名称 [
    <!ELEMENT person (name,age)>
    <!ELEMENT name (#PCDATA)
    <!ELEMENT age (#PCDATA)>
]>

3、 使用外部的dtd文件(网络上的dtd文件)

<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL" >

后面学到框架 struts2 使用配置文件使用:

< !DOCTYPE struts PUBLIC  "Apache softwate Foundateion//DTD Struts Configuration 2.0// EN"   "http://struts.apache.orp/dtds/struts-2.0.dtd">

定义元素

语法:

<!ELEMENT 元素名 约束>

1、简单元素(没有子元素的元素)

<! ELEMENT nane (#PCDATA)>

(#PCDATA):约束name是字符串类型,注意必须有括号,如“<name>张三</name>”
EMPTY:元素必须为空没有内容,如“<sex></sex>”
ANY:任意,可为空也可以不为空

2、复杂元素

<!ELEMENT 元素名称 (子元素1,子元素2,子元素3,子元素4)>

规则:
(1)子元素只能出现一次
(2)子元素出现多次怎么办:
    +  表示一次或者多次
    ?  表示零次或者一次
    *  表示零次或者多次(任意次数)
(3)子元素直接使用逗号进行隔开,逗号隔开的顺序表示子元素出现的顺序
(4)子元素直接使用|隔开,表示元素只能出现其中的任意一个(类似枚举)

定义属性

语法:

< !ATTLIST  元素名称  
    属性名称 属性类型 属性约束
    属性名称 属性类型 属性约束
    ...>

属性类型:

CDATA:表示属性的取值为普通文本字符串。例如:

ID1 CDATA #REQUIRED

ENUMERATED:表示枚举,表示方式为:(aa|bb|cc)

ID2 (red|green|blue) #REQUIRED

ID:表示属性取值不能重复,只能由字母、下划线开始,不能出现空白字符

ID3 ID #REQUIRED

属性约束说明:

#REQUIRED:表示该属性必须出现

ID1 CDATA #REQUIRED

#IMPLIED:表示该属性可有可无

ID1 CDATA #IMPLIED

#FIXED:表示属性的取值是一个固定值,语法格式:#FIXED "固定值"

ID1 CDATA #FIXED "ABC"

直接值:表示属性的取值为该默认值,写的话就按写的值设置

ID1 CDATA "2022"

定义实体

语法:

<!ENTITY 实体名称 "实体的值">

注意:定义实体需要写在内部 dta 里面,如果写在外部的 dtd 里面,有某些浏览器下,内容得不到

例子:

DTD中定义实体:
<!ENTITY copyright "版权归百度所有">

XML中使用实体:
&copyright; (注意有分号)

案例分析

<?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE TVSCHEDULE [
    <!ELEMENT TVSCHEDULE (CHANNEL+)>
    <!ELEMENT CHANNEL (BANNER,DAY+)>
    <!ELEMENT BANNER (#PCDATA)>
    <!ELEMENT DAY (DATE,(HOLIDAY|PROGRAMSLOT+)+)>
    <!ELEMENT HOLIDAY (#PCDATA)>
    <!ELEMENT DATE (#PCDATA)>
    <!ELEMENT PROGRAMSLOT (TIME,TITLE,DESCRIPTION?)>
    <!ELEMENT TIME (#PCDATA)>
    <!ELEMENT TITLE (#PCDATA)>
    <!ELEMENT DESCRIPTION (#PCDATA)>

    <!ATTLIST TVSCHEDULE NAME CDATA #REQUIRED>
    <!ATTLIST CHANNEL CHAN CDATA #REQUIRED>
    <!ATTLIST PROGRAMSLOT VTR CDATA #IMPLIED>
    <!ATTLIST TITLE RATING CDATA #IMPLIED>
    <!ATTLIST TITLE LANGUAGE CDATA #IMPLIED>
]>

<TVSCHEDULE NAME="CCTV1">
    <CHANNEL CHAN="cc0001">
        <BANNER>这里是banner</BANNER>
        <DAY>
            <DATE>2022-01-01</DATE>
            <HOLIDAY>这里是假期1</HOLIDAY>
        </DAY>
        <DAY>
            <DATE>2022-01-02</DATE>
            <PROGRAMSLOT VTR="ss001">
                <TIME>1998</TIME>
                <TITLE LANGUAGE="chinese">test title 111</TITLE>
                <DESCRIPTION>这里是DESCRIPTION</DESCRIPTION>
            </PROGRAMSLOT>
            <PROGRAMSLOT>
                <TIME>2008</TIME>
                <TITLE>test title 222</TITLE>
            </PROGRAMSLOT>
        </DAY>
    </CHANNEL>
</TVSCHEDULE>

三、XML解析:JAXP-dom

两种解析方式

xml 的解析技术:domsax

  • dom 方式解析(Document Object Model,文档对象模型):根据 xml 的层级结构在内存中分配一个树形结构,把 xml 的标签、属性、文件都封装成对象
    • 优点:非常方便实现做增删改操作;
    • 缺点:使用 dom 方式解析 xml 时候文件很大会造成内存溢出,不能进行分配;
  • sax 方式解析(Simple APl for XML,开源的):采用事件驱动,边读边解析,从上到下,一行一行的解析,解析到某一个对象返回对象名称
    • 优点:使用 sax 方式不会造成内存溢出因为它是边读边解析,从而实现查询;
    • 缺点:使用 sax 方式解析就不能实现增删改操作;

XML解析器:

  • JAXP:SUN标准解析,Sun公司提供了针对dom和sax解析器叫做 jaxp
  • Dom4J:开源解析开发包,针对dom和sax解析器 叫做 dom4j(用的最多)
  • JDom:开源解析开发包,针对dom和sax解析器叫做 jdom

JAXP的API

  • org.w3c.dom:提供 DOM 方式解析 XML 的标准接口
  • org.xml.sax:提供 SAX 方式解析 XML 的标准接口
  • javax.xml:提供了解析 XML 文档的类

javax.xml.parsers包中,定义了几个工厂类。我们可以通过调用这些工厂类,得到对XML文档进行解析的 DOM 和 SAX 解析器对象。

1、针对dom解析使用的类

DocumentBuilder :解析器类,这个类是一个抽象类,不能new

此类的实例可以从 DocumentBuilderFactory.newDocumentBuilder() 方法获取。

解析 xml:Document parse("xml路径") 。返回的 Document 是一个接口,父节点是 Node。

Document 方法:

    • getElementsByTagName(String tagname) 这个方法可以得到标签返回集合 NodeList
    • createElement(String tagName)创建标签元素
    • createrTextNode(String date)创建文本

Node里面的方法:

    • appendChild(Node newChild) 把文本添加在标签下面
    • removeChild(Node oldChild) 删除节点
    • getParentNode() 获取到父节点

NodeList里面的方法:

    • getLength() 得到集合的长度
    • item(int index) 根据下标取到的具体的值

DocumentBuilderFactory :解析器工厂,这个类一个抽象类,不能new

newInstance() 获取DocumentBuilderFactory的实例。

2、针对sax解析使用的类
SAXParser:解析器类,此类的实例可以从 SAXParserFactory.newSAXParser() 方法获得

SAXParserFactory:解析器工厂,抽象类,实例 newInstance() 方法得到

查询节点

package qa.music.demo25;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;

public class TestXml {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/test.xml");
        // 步骤4:得到所有的name元素
        NodeList names = document.getElementsByTagName("name");
        // 步骤5:返回集合,遍历集合,得到每一个name元素
        for (int i = 0; i < names.getLength(); i++) {
            System.out.println(names.item(i).getTextContent());
        }
    }
}

查询某一个节点

public class TestXml2 {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/test.xml");
        // 步骤4:得到所有的name元素
        NodeList names = document.getElementsByTagName("name");
        // 步骤5:使用下标,得到第一个元素
        Node name1 = names.item(0);
        System.out.println(name1.getTextContent()); // 张三
        System.out.println(name1.getNodeName()); // name
    }
}

添加节点

Transformer 是一个抽象类,实例需要回写TransformerFactory得到

TransformerFactory也是一个抽象类,实例是newInstance

package qa.music.demo25;

import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;

/**
 * 需求:在第一个p1下面(末尾)添加 <sex>nv</sex>
 */
public class TestXml3 {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, TransformerException {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/test.xml");
        // 步骤4:得到所有的p1元素
        NodeList list = document.getElementsByTagName("p1");
        // 步骤5:使用下标,得到第一个元素
        Node p1 = list.item(0);
        // 步骤6:创建sex标签:createElement
        Element sex = document.createElement("sex");
        // 步骤7:创建文本:createTextNode
        Text text = document.createTextNode("female");
        // 步骤8:把文本添加到sex下面:appendChild
        sex.appendChild(text);
        // 步骤9:把sex添加到第一个p1下面:appendChild
        p1.appendChild(sex);
        // 步骤10:回写xml
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        // 这里是将XMLSource转为Result
        transformer.transform(new DOMSource(document), new StreamResult("src/test.xml"));
    }
}

修改节点

/**
 * 需求:修改第一个p1下面的sex为 <sex>女</sex>
 */
public class TestXml4 {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, TransformerException {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/qa/music/demo25/test.xml");
        // 步骤4:得到sex
        Node sex = document.getElementsByTagName("sex").item(0);
        // 步骤5:修改sex
        sex.setTextContent("女");
        // 步骤6:回写xml
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("src/qa/music/demo25/test.xml"));
    }
}

删除节点

注意:只能用父节点进行删除,不能自己删除自己

/**
 * 需求:删除第二个 <age></age>
 */
public class TestXml5 {
    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException, TransformerException {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/qa/music/demo25/test.xml");
        // 步骤4:得到age
        Node age = document.getElementsByTagName("age").item(1);
        // 步骤5:得到age的父节点
        Node ageParentNode = age.getParentNode();
        // 步骤6:删除操作
        ageParentNode.removeChild(age);
        // 步骤7:回写xml
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult("src/qa/music/demo25/test.xml"));
    }
}

遍历节点

public class TestXml6 {
    public static void main(String[] args) throws Exception {
        // 步骤1:创建解析器工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 步骤2:根据解析器工厂创建解析器
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 步骤3:解析XML,返回document。执行parse方法得出Document
        Document document = builder.parse("src/qa/music/demo25/test.xml");
        // 步骤4:调用方法,实现遍历操作
        listElement(document, 0);
    }

    /**
     * 递归遍历的方法
     *
     * @param node
     * @throws Exception
     */
    public static void listElement(Node node, int deep) throws Exception {
        // 得到一层子节点
        NodeList childNodes = node.getChildNodes();
        // 判断如果是元素类型,则进行打印
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            deep++;
            for (int i = 1; i <= deep; i++) {
                System.out.print("\t");
            }
            System.out.println(node.getNodeName());
        }
        // 遍历子节点
        for (int i = 0; i < childNodes.getLength(); i++) {
            // 得到每一个节点
            Node node1 = childNodes.item(i);
            // 继续得到node1的子节点
            listElement(node1, deep);
        }
    }
}

四、Schema(代替DTD)

简介

XML Schema(本身是一个XML文件)是一种用于定义和描述XML文档结构与内容的模式语言,其出现是为了克服DTD的局限性(逐渐代替DTD)。特点:

  • 一个XML可以有多个Schema,使用名称空间来区分;
  • 支持更多数据类型,且支持自定义新的数据类型;
  • 但是不能像DTD一样定义实体;

扩展名一般是用.xsd,根节点是schema

Schema开发过程

例子:

schedemo.xsd

<?xml version="1.0" encoding="UTF-8"?>

<!--xmlns:表示当前xml文件是一个约束文件,这个是固定的-->
<!--targetNamespace:此属性表示引入,通常用url地址来引入-->
<!--elementFormDefault:此属性表示质量良好-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.baidu.cn/20220831"
        elementFormDefault="qualified">
  
  <element name="person">  <!--复杂元素-->
    <complexType> <!--复杂类型-->
      <sequence> <!--子元素顺序-->
        <element name="name" type="string"></element>  <!--简单元素-->
        <element name="age" type="int"></element>  <!--简单元素-->
        <element name="sex" type="string"></element>  <!--简单元素-->
        <!--此子元素也是一个复杂元素-->
        <element name="address">
          <complexType>
            <sequence>
              <element name="home" type="string"></element>
              <element name="company" type="string"></element>
            </sequence>
          </complexType>
        </element>
      </sequence>
    </complexType>
  </element>
  
</schema>

person.xml

<?xml version="1.0" encoding="utf-8" standalone="no"?>

<!--表示是被约束文件:-instance,等于xmlns+(-instance) -->
<!-- xmlns:xsi 起了一个别名空间叫xsi,等于targetNamespace -->
<!-- 引入约束文件{namespace}空格{location},等于targetNamespace+schema地址 -->
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.baidu.cn/20220831"
        xsi:schemaLocation="http://www.baidu.cn/20220831 schedemo.xsd">

    <name>张三</name>
    <age>20</age>
    <sex></sex>
    <address>
        <home>杭州西湖区</home>
        <company>杭州余杭区</company>
    </address>
</person>

Schema约束API查看

schema的结构:

它的根节点是Schema,里面要加上三个属性。使用时首先要判断它是简单元素还是复杂元素,如果是一个复杂元素,先写complexType再写sequence然后再写上子元素。如果是简单元素就直接写element。

复杂元素指示器:

:表示元素的出现的顺序

:表示所有元素都只能出现一次

:表示只能出现其中一个

:表示任意配置的元素

maxOccurs="unbounded"(加在element上):表示元素出现次数没限制


在Schema里可以约束属性(必须是复杂元素,写在结束标签之前

<attribute nane="id1" type="int" use="required">name

①name:属性名称

②type:属性类型 int stirng

③use:属性是否必须出现 required

xmlns:xsi 是加一个名称空间

例子:

<?xml version="1.0" encoding="UTF-8"?>

<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.baidu.cn/20220831"
        elementFormDefault="qualified">
    <element name="note">
        <complexType>
            <sequence>
                <element name="to" type="string"/>
                <element name="from" type="string"/>
                <element name="heading" type="string"/>
                <element name="body" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>
<?xml version="1.0" encoding="utf-8" ?>

<note xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.baidu.cn/20220831"
        xsi:schemaLocation="http://www.baidu.cn/20220831 message.xsd">
    <to>George</to>
    <from>Mikel</from>
    <heading>meeting remember</heading>
    <body>Please Don't forget!</body>
</note>

sax解析过程

SAXParser:解析器类,此类的实例可以从 SAXParserFactory的newSAXParser() 方法获得

SAXParserFactory:解析器工厂,抽象类,实例 newInstance() 方法得到


SAXParser里面的解析方法:

parse(File f, DefaultHandler dh)

参数为:File为xml的路径、DefaultHandler为事件处理器(默认的API处理器,相当于在方法里面绑定了一个事件)

DefaultHandler里面的方法:

(1)startElement()

startElement(String uri, String localName, String qName, Attributes attributes) 接收元素开始的通知

参数qName:返回标签名称

(2)endDocument()

endElement(String uri, String localName, String qName) 接收元素结束的通知

通过string的构造方法返回内容

(3)characters

characters(char [] ch, int start, int length) 接收元素中字符数据的通知,就是文本的内容

参数qName:返回标签名称

使用schema的sax方式操作xml

sax方式不能实现增删改操作,只能做查询操作

例子:

public class TestSax {
    public static void main(String[] args) throws Exception {
        // 1、创建解析器工厂
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        // 2、创建解析器
        SAXParser saxParser = saxParserFactory.newSAXParser();
        // 3、执行parse方法,DefaultHandler里面的方法就会自动去执行
        // 输出整个文档
        saxParser.parse("src/p1.xml", new MyDefaultHandler());
        // 输出name元素的值
        saxParser.parse("src/p1.xml", new MyDefaultHandler2());
    }
}
/*打印整个文档*/
public class MyDefaultHandler extends DefaultHandler {
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        System.out.print("<" + qName + ">");
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        System.out.print(new String(ch, start, length));
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        System.out.print("</" + qName + ">");
    }

}
/**打印name元素的值*/
public class MyDefaultHandler2 extends DefaultHandler {
    boolean flag = false;
    int index = 1;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if ("name".equals(qName)) {
            flag = true;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (flag == true && index == 2) {
            System.out.println(new String(ch, start, length));
            flag = false;
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("name".equals(qName)) {
            flag = false;
            index++; // 这个name结束,索引需要加一
        }
    }

}

五、XML解析:dom4j(重点)

概述

jar包下载地址:dom4j.github.io/

基本使用

解析XML

import java.net.URL;

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

public class Foo {
    public Document parse(URL url) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(url);
        // 返回document,更加方便
        return document;
    }
}

使用迭代器

public void bar(Document document) throws DocumentException {

    Element root = document.getRootElement();

    // iterate through child elements of root
    for (Iterator<Element> it = root.elementIterator(); it.hasNext();) {
        Element element = it.next();
        // do something
    }

    // iterate through child elements of root with element name "foo"
    for (Iterator<Element> it = root.elementIterator("foo"); it.hasNext();) {
        Element foo = it.next();
        // do something
    }

    // iterate through attributes of root
    for (Iterator<Attribute> it = root.attributeIterator(); it.hasNext();) {
        Attribute attribute = it.next();
        // do something
    }
 }

XPath导航

public void bar(Document document) {
    List<Node> list = document.selectNodes("//foo/bar");
    Node node = document.selectSingleNode("//foo/bar/author");
    String name = node.valueOf("@name");
}

例如在在XHTML文档中找到所有超文本链接:

public void findLinks(Document document) throws DocumentException {

    List<Node> list = document.selectNodes("//a/@href");

    for (Iterator<Node> iter = list.iterator(); iter.hasNext();) {
        Attribute attribute = (Attribute) iter.next();
        String url = attribute.getValue();
    }
}

创建新的XML文档

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

public class Foo {

    public Document createDocument() {
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement("root");

        Element author1 = root.addElement("author")
            .addAttribute("name", "James")
            .addAttribute("location", "UK")
            .addText("James Strachan");

        Element author2 = root.addElement("author")
            .addAttribute("name", "Bob")
            .addAttribute("location", "US")
            .addText("Bob McWhirter");

        return document;
    }
}

将文档写入文件

FileWriter out = new FileWriter("foo.xml");
document.write(out);
out.close();

查询XML

package qa.music.demo27;

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

import java.util.List;

public class TestDom4j {
    public static void main(String[] args) throws Exception {
        //selectName();
        //selectFirst();
        selectSecond();
    }

    /**
     * 查询xml中所有name元素的值
     */
    public static void selectName() throws Exception {
        // 1.创建解析器
        SAXReader saxReader = new SAXReader();
        // 2.得到document
        Document document = saxReader.read("src/p1.xml");
        // 3.得到根节点
        Element rootElement = document.getRootElement();
        // 4.得到p1
        List<Element> p1list = rootElement.elements("p1");
        // 5.遍历list,用增强for循环
        for (Element element : p1list) {
            // 6.得到p1下面的name
            Element name = element.element("name");
            // 7.得到name里面的值
            System.out.println(name.getText());
        }
    }

    /**
     * 查询第一个name元素的值
     */
    public static void selectFirst() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/p1.xml");
        Element rootElement = document.getRootElement();
        // 得到第一个p1
        Element p1 = rootElement.element("p1");
        // 得到p1下面的name元素
        Element name = p1.element("name");
        System.out.println(name.getText());
    }

    /**
     * 查询第二个name元素的值
     */
    public static void selectSecond() throws Exception {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/p1.xml");
        Element rootElement = document.getRootElement();
        // 得到所有p1
        List<Element> p1list = rootElement.elements("p1");
        // 得到第二个name
        Element name = p1list.get(1).element("name");
        System.out.println(name.getText());

    }
}

末尾添加节点

public class TestDom4j2 {
    public static void main(String[] args) throws Exception {
        addSex();
    }

    /**
* 第一个p1标签末尾添加一个元素<sex>nv</sex>
*/
    public static void addSex() throws Exception {
        // (1) 创建解析器
        SAXReader saxReader = new SAXReader();
        // (2) 得到document
        Document document = saxReader.read("src/p1.xml");
        // (3) 得到根节点
        Element rootElement = document.getRootElement();
        // (4) 获取到第一个p1
        Element p1 = rootElement.element("p1");
        // (5) 在p1下面添加元素
        Element sex = p1.addElement("sex");
        // (6) 在添加完成之后的元素下面添加文本
        sex.setText("male");
        // (7) 回写xml(因为是对xml文件内进行操作)
        OutputFormat format = OutputFormat.createPrettyPrint(); // 格式化
        XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/qa/music/demo27/p1.xml"), format);
        xmlWriter.write(document);
        xmlWriter.close();
    }
}

特定位置添加节点

public class TestDom4j3 {
    public static void main(String[] args) throws Exception {
        addAgeBefore();
    }

    /**
     * 第一个p1下面的age标签之前添加<school>国防科技大学</school>
     */
    public static void addAgeBefore() throws Exception{
        // 创建解析器
        SAXReader saxReader = new SAXReader();
        // 得到document
        Document document = saxReader.read("src/p1.xml");
        // 得到根节点
        Element rootElement = document.getRootElement();
        // 获取到第一个p1
        Element p1 = rootElement.element("p1");
        // 获取p1下面的所有的元素
        List<Element> list = p1.elements();
        // 创建元素在元素下面创建文本
        Element school = DocumentHelper.createElement("school");
        school.setText("国防科技大学");
        // 使用list里面的方法,在特定位置添加元素
        list.add(1, school);
        // 回写xml
        OutputFormat format = OutputFormat.createPrettyPrint(); // 格式化
        XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("src/qa/music/demo27/p1.xml"), format);
        xmlWriter.write(document);
        xmlWriter.close();
    }
}

\

常用代码封装

public class Dom4jUtil {

    /**
     * 解析返回Document
     * @param path xml文件路径
     * @return Document
     * @throws Exception
     */
    public static Document parse(String path) throws Exception{
        // 创建解析器
        SAXReader saxReader = new SAXReader();
        // 得到document
        Document document = saxReader.read(path);
        return document;
    }

    /**
     * 回血XML
     * @param path xml文件路径
     * @param document Document
     * @throws Exception
     */
    public static void xmlWriters(String path, Document document) throws Exception{
        OutputFormat format = OutputFormat.createPrettyPrint(); // 格式化
        XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(path), format);
        xmlWriter.write(document);
        xmlWriter.close();
    }
}

测试工具类:

public class TestDom4j5 {
    public static void main(String[] args) throws Exception {
        demo();
    }

    /**
     * 查询xml中所有name元素的值
     */
    public static void demo() throws Exception {
        Document document = Dom4jUtil.parse("src/p1.xml");

        Element rootElement = document.getRootElement();
        Element p1 = rootElement.element("p1");
        Element sex = p1.addElement("address");
        sex.setText("浙江杭州");

        Dom4jUtil.xmlWriters("src/p1.xml", document);
    }
}

修改节点

public class TestDom4j6 {
    public static void main(String[] args) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);

        Element rootElement = document.getRootElement();
        Element p1 = rootElement.element("p1");
        Element age = p1.element("age");
        age.setText("88");

        Dom4jUtil.xmlWriters(Dom4jUtil.PATH, document);
    }
}

\

删除节点

public class TestDom4j7 {
    public static void main(String[] args) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);

        Element rootElement = document.getRootElement();
        Element p1 = rootElement.element("p1");
        Element address = p1.element("address");
        p1.remove(address);

        Dom4jUtil.xmlWriters(Dom4jUtil.PATH, document);
    }
}

\

获取属性值

public class TestDom4j8 {
    public static void main(String[] args) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);

        Element rootElement = document.getRootElement();
        Element p1element = rootElement.element("p1");

        // 获取p1里面的属性值
        String id = p1element.attributeValue("id");
        System.out.println(id);
    }
}

\

\

六、XPATH

简介

一个/表示一层:

  • /aaa 表示第一层,第一层aaa就可以取到
  • /aaa/ccc 表示aaa层下面的ccc(某一层下面的所有某元素
  • /aaa/ddd/bbb 表示aaa下面的ddd的bbb元素

两个/表示任意层:

  • //bbb 表示:和这个名称相同,只要名称是BBB都可以得到,不管在哪层
  • //ddd/bbb 表示所有DDD下面的BBB都得到

"*"表示所有元素:

  • /aaa/ccc/ddd* 表示aaa中的ccc中的ddd里面的所有元素
  • ///*/bbb 表示前三层下的bbb元素
  • //* 表示所有的元素

[]表示坐标索引的元素:

  • /aaa/bbb[1] 表示aaa元素里的第一个bbb
  • /aaa/bbb[last()] 表示表示aaa元素里的最后一个bbb

@表示带有某属性的元素:

  • //@id 表示只要标签上有id属性都可以得到
  • //bbb[@id] 表示只要bbb上面有id属性都可以得到
  • //bbb[@id='b1'] 表示元素名称是bbb,在bbb元素里面有id属性,id的值是b1
  • //aaa[@name='bbb'] 表示aaa下面有name属性,name的值是bbb

dom4j的XPATH操作

jaxen的jar包下载地址:www.java2s.com/Code/Jar/j/…

支持xpath的方法:

  • selectNodes("xpath表达式") ---获取多个节点
  • selectSingleNode(“xpath表达式”) ---获取一个节点
public class TestDom4j9 {
    public static void main(String[] args) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);

        // 得到所有的name元素
        List<Node> nodeList = document.selectNodes("//name");
        for (Node node : nodeList) {
            System.out.println(node.getText());
        }
    }
}
public class TestDom4j10 {
    public static void main(String[] args) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);
        
        // 获取第一个p1下面的name属性
        Node node = document.selectSingleNode("//p1[@id='aaa']/name");
        System.out.println(node.getText());
    }
}

七、学生管理系统案例

student.xml

<student>
    <stu>
        <id>100</id>
        <name>ZhangSan</name>
        <age>20</age>
    </stu>
    <stu>
        <id>101</id>
        <name>LiSi</name>
        <age>21</age>
    </stu>
</student>

功能类:

public class StuService {
    /**
     * 增加学生
     *
     * @param student
     * @throws Exception
     */
    public static void addStu(Student student) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);
        // 得到根节点
        Element rootElement = document.getRootElement();
        // 在根节点上面添加stu
        Element stu = rootElement.addElement("stu");
        // 在stu标签上面依次添加id name age标签 和对应的值
        Element id = stu.addElement("id");
        id.addText(student.getId());
        Element name = stu.addElement("name");
        name.addText(student.getName());
        Element age = stu.addElement("age");
        age.addText(student.getAge());
        // 回写xm1
        Dom4jUtil.xmlWriters(Dom4jUtil.PATH, document);
    }

    /**
     * 删除学生
     * @param id
     * @throws Exception
     */
    public static void deleteStu(String id) throws Exception {
        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);
        // 获取所有的id,xpath: //id
        List<Node> list = document.selectNodes("//id");
        for (Node node : list) {
            if (id.equals(node.getText())) {
                // 得到stu节点
                Element stu = node.getParent();
                // 获取stu的父节点,删除这个id的整个stu
                Element parent = stu.getParent();
                parent.remove(stu);
            }
        }
        // 回写xm1
        Dom4jUtil.xmlWriters(Dom4jUtil.PATH, document);
    }

    /**
     * 查询学生
     * @param id
     * @return Student
     * @throws Exception
     */
    public static Student getStu(String id) throws Exception {
        Student student = null;

        Document document = Dom4jUtil.parse(Dom4jUtil.PATH);
        // 获取所有的id,xpath: //id
        List<Node> list = document.selectNodes("//id");
        for (Node node : list) {
            if (id.equals(node.getText())) {
                // 得到id的父节点stu
                Element stu = node.getParent();
                // 通过stu获取name和age
                String name = stu.element("name").getText();
                String age = stu.element("age").getText();
                student = new Student(id, name, age);
            }
        }
        return student;
    }
}