09- XML 语法基础与解析技术

XML 技术

XML 的概念

可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。

XML 和 HTML的关系

联系:
     1、从外观上看,名字里都有一个“ML”,就是标记语言的意思,这不得不说是一个相似点吧?
     不错,它们在名字上相似,在结构上也非常像,事实上XML是对HTML的补充。
     2、转换:为了更好地适应client浏览器的浏览,能够在server端进行XML->HTML的转换,但普通静态控件不支持这样的转换,能够选择支持XMLDOM的ASP或PHPserver进行。
     3、操作性:它们自身都不进行操作,XML描写叙述数据,HTML显示。
    
差别:
     1、目的:XML是用来描写叙述、传输和存储数据的,即,关于什么是数据及怎样存放的问题,焦点在数据的内容;
         HTML是用来显示数据的,即,关于怎样显示及较好地显示的问题,焦点在数据的外观。
     2、语法:完整性方面,XML要求全部标记都要成对出现,HTML显然不是,比方img、input等,能够无结束标签;
         大写和小写方面,HTML不区分大写和小写,而XML严格区分。
     3、自主性:即能否够自定义。XML标记由架构或文档作者自定义,没有什么限制;HTML则不行,仅仅能用提前定义的,即head、body等。

xml 文档结构

声明 + 根标签 (子标签)

有且仅有有一个根标签

<?xml version="1.0" encoding="UTF-8" ?>
<books>
    <book>
        <author></author>
        <title></title>
        <description></description>
    </book>
    <book>
        <author></author>
        <title></title>
        <description></description>
    </book>
</books>

标签可以自定义添加

书中 有 作者, 标题, 描述, 页数, 出版社, 出版日期, 价格

自定义案例

描述一个人的午餐需求

<?xml version="1.0" encoding="UTF-8" ?>
<午餐>
    <主食>意大利面</主食>
    <菜品>
        <菜品1>法式焗蜗牛</菜品1>
        <菜品2>法国鹅肝</菜品2>
        <菜品3>顶配鱼子酱</菜品3>
        <菜品4>神户雪花牛肉</菜品4>
    </菜品>
    <汤>松茸南瓜汤</汤>
    <甜点>提拉米苏</甜点>
</午餐>

标签语法标准

标签元素

属性

标签组成 <标签名 属性= “值”>内容</标签名>

语法要求

标签名  必须使用<></>  必须成对出现 (有开始有结束)
标签名 大小写敏感(区分大小写)
元素名称不能以数字或者标点符号开始 也不能包含空格或冒号
XML 不能交叉嵌套
属性	属性值需要使用 双引号 包裹
	一个元素可以有多个属性
	属性值不能包含特殊字符(  <  "  & )

软性要求

良好的缩进习惯

特殊字符

符号 表示方式
< &lt(😉
> &gt
& &amp
" &quot
&apos

衣服尺码案例

<?xml version="1.0" encoding="UTF-8" ?>
<size>
    <S>身高&lt;165</S>
    <M>165&lt;身高&lt;170</M>
    <L>170&lt;身高&lt;175</L>
    <XL>175&lt;身高&lt;180</XL>
    <XXL>180&lt;身高&lt;185</XXL>
</size>

注释

<!-- 注释内容 -->

xml优势

  • 数据存储 (保存衣服尺码, 学生分数, 书籍信息)
  • 数据交换
  • 数据配置 (因为可以保存各种格式的信息, 很多软件使用xml 保存软件的配置信息)

命名空间

在XML 中 用作元素或属性名称的名称集合, 用来标识来自特定域的名称

问题 小米 是什么意思
    小米是一种粮食
    小米是一款手机
问题 table 是什么意思
	一张桌子
	一张表格
同一个词在不同的场景下有不同的含义
我们为了区分不同的场景, 引入了命名空间
	地里面长出来的小米 是什么意思
	科技公司小米公司中的小米 是什么意思
	英语单词中 table 是什么意思
	HTML 语言中 table 是什么意思

必要性

XML 解析器在解析XML 文档的时候, 对于重名的元素, 可能出现解析冲突, 命名空间有助于标准化元素和属性, 为它们添加唯一的标识

声明语法

ns namespace

<根标签  xmlns:命名空间=命名空间的地址信息> 

案例

<?xml version="1.0" encoding="UTF-8" ?>
<students
    xmlns:middle="http://www.middle.com"
    xmlns:university="http://www.university.com">
    <middle:stu>
        <name>三毛</name>
        <age>13</age>
    </middle:stu>
    <university:stu>
        <name>大毛</name>
        <age>22</age>
    </university:stu>
</students>

属性也可以使用命名空间

 <stu name="张三" nick:name="三"></stu>

XML 约束

XML 因为是可扩展的, 不能无限制的书写, 需要通过约束加以规范

DTD 约束

简介

Document Type Definition 文档类型定义
DTD是 xml文档 对自身格式的一个描述, 可以使用DTD校验文档数据的有效性

作用

规则

1- 元素的定义规则
2- 元素之间的关系规则
3- 属性的定义规则

语法

声明

<!DOCTYPE 根元素 [定义的内容]>

元素

<!ELEMENT 标签名 元素类型> 

属性

<!ATTLIST 元素名
    属性名1 属性类型 设置说明
    属性名2 属性类型 设置说明
...>

属性类型

CDATA:属性值为任意文本数据;
Enumerated:属性值必须是枚举列表中的一个;
ID:属性值必须是唯一的,并且属性值不能以数字开头;

设置说明

1:#REQUIRED,表示属性是必须的。
2:#IMPLIED,表示属性是可选的。

实体

<!ENTITY CCTV "中国中央电视台">
必须使用内部引入的方式, 才能够正常显示
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE poems SYSTEM "poems.dtd"[
        <!ENTITY CCTV "中国中央电视台">]>
<poems>
    <poem time="唐代" type="五言律诗">
        <author>王维</author>
        <title>鹿柴</title>
        <content>&CCTV;</content>
    </poem>
</poems>

案例

案例代码

<?xml version="1.0" encoding="UTF-8" ?>
<poems>
    <poem>
        <author>王维</author>
        <title>鹿柴</title>
        <content>空山不见人,但闻人语响.</content>
        <page>500</page>
    </poem>
</poems>

约束代码

<!DOCTYPE poems [
        <!ELEMENT poems (poem) >
        <!ELEMENT poem (author,title,content,page)>
        <!ELEMENT author (#PCDATA)>
        <!ELEMENT title (#PCDATA)>
        <!ELEMENT content (#PCDATA)>
        <!ELEMENT page (#PCDATA)>
]>

DTD约束中的符号

符号 含义 实例 实例说明
() 用来给元素分组 (author,title,content,page) 元素中的子元素组
| 从列出的对象中
选择其中之一
(author|title) author 或者 title 其中之一
, 对象按照指定顺序
全部出现
(author,title) 必须全部按照顺序出现
* 该对象允许
零次或多次
(author*,title) author标签可以不出现,也可以出现多次
? 该对象允许
零次或一次
(author?,title) author标签可以不出现,也可以最多出现1次
+ 该对象允许
一次或多次
(author+,title) author标签最少出现1次

添加属性约束

<?xml version="1.0" encoding="UTF-8" ?>
<poems>
    <poem time="唐代" type="五言律诗">
        <author>王维</author>
        <title>鹿柴</title>
        <content>空山不见人,但闻人语响.</content>
        <page>500</page>
    </poem>
</poems>

添加属性约束

<!ATTLIST poem
        time CDATA #REQUIRED
        type CDATA #IMPLIED>

引用方式

内部定义

外部定义

1- 在外部定义一个后缀名为dtd 的文件
2- 把约束内容复制到 dtd文件中
3- 在xml 中 引入 dtd 约束文件
<!DOCTYPE poems SYSTEM "poems.dtd">
<!DOCTYPE 根标签 SYSTEM "文件路径">

外部引入网络文件

<!DOCTYPE 根标签 PUBLIC "文件路径">

Schema 约束

Schema 约束的概念

用一套 预先规定的XML 元素和属性创建的约束, 这些元素和属性, 定义了XML 稳当的结构和内容模式

相比于DTD的优势

Schema 相对于DTD约束 
- 更加丰富的数据类型
- 更加友好的 约束格式 (约束文档格式也是 XML 的格式 , 内行人领导内行人)
- Schema 支持命名空间
- 定义约束的能力非常强大,可以对XML实例文档作出细致的语义限制。 

语法格式

文档后缀名 .xsd

是完全符合 XML 语法的

根元素 为 schema

命名空间

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

案例代码

xml 文件

<customer>
    <name>张三</name>
    <address>XXX省XX市 ... </address>
</customer>

xsd文件格式

?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://mynamespace/mynamespace"
            elementFormDefault="qualified">
    <xsd:element name="customer">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="name" type="xsd:string"/>
                <xsd:element name="address" type="xsd:string" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

解析

xsd 文件中 
1- xml:xsd   该约束文件所依赖约束位置
该文件锁需要的元素和数据类型, 是来自于 http://www.w3.org/2001/XMLSchema 命名空间
2- targetNamespace  该约束文件所定义的 元素和数据类型 会添加到该命名空间
	xml 文件 如果想要受到该文件约束, 就需要使用 命名空间引入 该空间
3- elementFormDefault=unqualified 目标命名空间不一定必须遵循该Schema 约束 
	(必须循序使用 elementFormDefault=qualified)

xml引入 xsd约束文件的方式

本地引入

<customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="customer.xsd">
    <name>张三</name>
    <address>XXX省XX市 ... </address>
</customer>

命名空间引入

<customer xmlns="http://mynamespace/mynamespace"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://mynamespace/mynamespace customer.xsd">
    <name>张三</name>
    <address>XXX省XX市 ... </address>
</customer>

解析

1-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance 
表示 该xml文件是一个被约束的文件
2-  xsi:schemaLocation 一般的值为 targetNameSpace 空格 约束文档的地址路径(名称)

Schema 元素类型

根元素

schema 
xsd 文件中 
1- xml:xsd   该约束文件所依赖约束位置
该文件锁需要的元素和数据类型, 是来自于 http://www.w3.org/2001/XMLSchema 命名空间
2- targetNamespace  该约束文件所定义的 元素和数据类型 会添加到该命名空间
	xml 文件 如果想要受到该文件约束, 就需要使用 命名空间引入 该空间
3- elementFormDefault=unqualified 目标命名空间不一定必须遵循该Schema 约束 
	(必须循序使用 elementFormDefault=qualified)

element 元素

用于声明一个标签(元素) 

attribute 元素

定义一个属性

简单类型 simpleType

复杂类型 complexType

1- 序列 sequence
2- 选择 choice

XML 解析技术

之前的学习, 是XML 如何存储数据的 , 解析技术 是为了从格式良好的XML 文档中获取数据

解析技术分类

DOM

Document Object Model 文档对象模型

文档树

把一个格式良好的 xml文档 看做是一颗节点数, 根节点为树根, 每一个子节点都是一条树枝

DOM

基于XML文档树结构的解析
适用于多次访问的XML文档
特点:比较消耗资源

JDOM

DOM4J

DOM for java 
非常优秀的Java XML API
性能优异、功能强大
开放源代码

SAX

基于事件的解析
适用于大数据量的XML文档
特点:占用资源少,内存消耗小

SAX 和 DOM 的对比

DOM 把整个文档全部加载到内存中 , 根据指定的方法获取文档树的节点
SAX 一行一行的执行, 当执行到某些具体的,带有某些标志性的行的时候, 执行相应的事件

案例

一麻袋硬币
数一数一共有多少枚硬币
1- 先称出 一个硬币的重量, 再称所有硬币的重量 , 两者相除, 所得即为硬币的数量
	(效率很高, 占用内存大, 需要有人提起所有硬币秤一下)
2- 也可以一个一个数数量
	(效率较低, 用时较长, 但是占用内存小, 查的人每一次手里只需要握有一枚硬币)

在这里插入图片描述

使用DOM 解析 XML

案例准备

<?xml version="1.0" encoding="UTF-8" ?>
<PhoneInfo>
    <Brand name="华为">
        <Type name="U8650"></Type>
    </Brand>
    <Brand name="苹果">
        <Type name="iPhone4"></Type>
        <Type name="iPhone5"></Type>
    </Brand>
    <Brand name="小米">
        <Type name="note 7 pro"></Type>
        <Type name="小米9"></Type>
    </Brand>
</PhoneInfo>

步骤分析

1- 创建DOM对象 (解析器)
2- 通过解析器对象, 解析整个XML文档, 获取整个文档的 DOM树对象
3- 通过对 DOM树对象, 对整个XML 文档 完成增删改查操作

关键代码

package s0809;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
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 Demo1_Dom {

    public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
        // 创建DOM解析器
        // 1- 先创建一个工厂
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        // 2- 通过工厂生成解析器
        DocumentBuilder db = dbf.newDocumentBuilder();
        // 通过解析器加载 xml 文档
        Document doc = db.parse("src\\s0809\\phone.xml");
        // 通过DOM 树 完成 对XML 文档的增删改查操作
        // 1- 查询操作
        // 根据标签名 获取 节点对象
        NodeList brandList = doc.getElementsByTagName("Brand");
        // 循环遍历获取每一个 brand 的内容
        for(int i = 0 ; i < brandList.getLength(); i++){
            //NodeList.item(i) 相当于 List.get(i)
            Node brand = brandList.item(i);
            // 节点 包括(元素节点  文本节点 注释节点 等等 )
            // 把节点转换为 标签对象
            Element ele = (Element) brand;
            // 获取该标签的属性
            String name = ele.getAttribute("name");
            System.out.println(name);
            // 获取每一个品牌的 所有子元素标签
            NodeList typeList = ele.getChildNodes();
            for(int k = 0 ; k < typeList.getLength(); k++){
                // 遍历获取每一个 type 标签
                Node type = typeList.item(k);
                if(type instanceof Element){
                    Element typeEle = (Element)type;
                    String typeName = typeEle.getAttribute("name");
                    System.out.println(name+"--"+typeName);
                }
            }
        }

    }
}

Document常用方法

getElementsByTagName("Brand")   根据标签名获取该标签对象的集合
doc.createElement()		创建元素对象
doc.createAttribute()	创建属性对象

Element 标签对象的常用方法

Element 方法 描述说明
ele.getTagName()
ele.getNodeValue()
ele.getAttribute(“name”) 根据属性名称获取属性值
ele.setAttribute();
ele.hasAttribute()
ele.removeAttribute();
ele.getChildNodes() 获取所有子节点对象
ele.appendChild()
ele.removeChild()
ele.replaceChild()

给手机收藏添加新手机

//  添加 oppo 手机信息    Reno 
<Brand name = "oppo">
	<type name="Reno">

案例代码

提取公共方法

public static Document getDocument()throws Exception{
    // 创建DOM解析器
    // 1- 先创建一个工厂
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    // 2- 通过工厂生成解析器
    DocumentBuilder db = dbf.newDocumentBuilder();
    // 通过解析器加载 xml 文档
    Document doc = db.parse("src\\s0809\\phone.xml");
    return doc;
}

测试添加方法

//添加 元素
public static void testAdd()throws  Exception{
    // 获取Doc 对象
    Document doc = getDocument();
    // <Brand name = "oppo">
    Element brand = doc.createElement("Brand");
    brand.setAttribute("name","Oppo");
    // 给手机品牌设置 子标签 Type
    //<type name="Reno">
    Element type = doc.createElement("Type");
    type.setAttribute("name","Reno");

    // 把Type 标签 添加到 brand 中
    brand.appendChild(type);
    // 把 brand 添加到 根标签中
    // 获取根标签
    NodeList phoneInfo = doc.getElementsByTagName("PhoneInfo");
    Element info = (Element) phoneInfo.item(0);
    info.appendChild(brand);
    // 仅仅是在内存中 包含关系已经确定了, 如何把内存中的内容 持久化到硬盘文件中

    // 持久化流程
    // 1- 创建持久化对象工厂
    TransformerFactory tf = TransformerFactory.newInstance();
    // 2- 创建持久化对象
    Transformer transformer = tf.newTransformer();
    // 把Document对象 包装成  domSource 对象
    DOMSource domSource = new DOMSource(doc);
    // 设置编码格式
    transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");
    // s设置输出流资源
    StreamResult result = new StreamResult(new FileOutputStream("src\\s0809\\phone.xml"));
    transformer.transform(domSource,result);

}

DOM4J 解析XML

DOM4J 不属于 java 内置的 程序包, 属于第三方 (Junit 需要引入第三方Jar 包)

查询

使用DOM4J 读取 Phone.xml的内容

// 遍历phone.xml 中的信息
public static void seachAll() throws DocumentException {
    // 新建解析器对象
    SAXReader reader = new SAXReader();
    // 获取DOM 文档对象
    Document read = reader.read("src\\s0809\\phone.xml");
    // 使用DOM4J解析 必须先获取根节点
    Element root = read.getRootElement();
    //根据根节点 获取所有子节点
    List<Element> brandList = root.elements();
    // 通过 for each 循环 遍历子节点
    for(Element brand : brandList){
        // 获取 节点名称
        // System.out.println(brand.getName());
        // 获取该节点的 属性对象
        Attribute brandAttr = brand.attribute("name");
        System.out.println(brandAttr.getValue());
        // 获取品牌之下的 子节点 型号标签
        List<Element> types = brand.elements();
        for(Element type : types){
            Attribute typeAttr = type.attribute("name");
            System.out.println("\t"+typeAttr.getValue());
        }
    }
}

新增节点

public static void  add()throws Exception{
    // 新建解析器对象
    SAXReader reader = new SAXReader();
    // 获取DOM 文档对象
    Document read = reader.read("src\\s0809\\phone.xml");
    // 使用DOM4J解析 必须先获取根节点
    Element root = read.getRootElement();

    // 给 根节点 添加一个子节点 Brand
    Element brand = root.addElement("Brand");
    brand.addAttribute("name","VIVO");
    Element type = brand.addElement("Type");
    type.addAttribute("name","X23");


    // 设置编码格式
    OutputFormat format = OutputFormat.createPrettyPrint();
    format.setEncoding("UTF-8");
    // 把DOM 持久化到文件中
    XMLWriter writer = new XMLWriter(new FileWriter("src\\s0809\\phone.xml"),format);
    writer.write(read);
    writer.close();

}

思考 把XML 转换为 对象

新建一个Phone 类

有name 属性

思考 如何 解析 XML 的同时 创建 Phone 的对象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章