第3讲.XML基础/XML解析

XML基础/XML解析

为什么要学XML

XML是一种通用的数据交换格式

许多系统的配置文件都使用XML格式

xml可以跨平台,主流各种平台都对xml有支持, 真正的跨平台.

xml 是各种应用程序之间进行数据传输的最常用的工具。

掌握XML是软件开发人员的一项基本技能

任意一个javaee框架中都要用到XML。

XML结构清晰(树状结构),不仅让人能够明白,还让计算机也能够明白。

XML作为一种公订的、开放的标准,不受知识产权的限制.(免费)

具有结构性的数据应该使用xml来存取,这样看上去结构更加的清晰(如省市区的数据)

XML是什么

XML(eXtensible Markup Language),是一种可扩展的标记语言,类似HTML。如: <h1></h1>

XML技术是W3C组织(World Wide Web Consortium万维网联盟)发布的,目前遵循的是W3C组织于1998年发布的XML1.0规范。

XML被广泛认为是继Java之后在Internet上最激动人心的新技术。

HTML: 显示页面,网页. 学习里面自带的标签<html>

XML: 主要是用于描述配置信息。

XML标签没有被预定义,需要用户自行定义标签。

XML的语法

    1. 文档声明:

在编写XML文档时,需要先使用文档声明来声明XML文档。且必须出现在文档的第一行。

最简单的语法:

如:<?xml version="1.0"?>

用encoding属性说明文档所使用的字符编码,默认为UTF-8。

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

用standalone属性说明文档是否独立,即是否依赖其他文档。yes表示独立文档不依赖,no表示要依赖,可以不写则表示可以依赖可以不依赖

如:<?xml version="1.0" standalone="no"?>
    1. 编码:XML有两个编码: 保存在磁盘上的文件编码要与声明的编码一致。要保证两个编码相同,都为UTF-8

内容编码:文档声明中指定

文件本身的编码:文件另存为时可以选择

    1. 元素(标签)<>(可以自定义标签)

XML文档必须有一个根元素.

标签区分大小写的.<a> <A>

有开始标签就必须有结束标签.<linkman></linkman> 或者 自闭合<linkman/>

不允许标签交叉嵌套

    1. 属性:

属性是元素的一部分,它必须出现在元素的开始标签中

key=value格式,尽量在编写的时候 value 前后不要有空格

属性的key不能使用空格,冒号特殊字符.

属性的key需要以字母开头.

属性的value必须使用引号或者双引号

  • 5.注释

      如:<!-- 注释 -->
    
  • 6.命名规则

在XML中,可以使用自己需要的元素来扩展标记语言。

命名基本规则:

·元素名称不能以数字或特殊字符开头

·可以包含字母,数字,下划线等

·不能以字符串“XML”作为开头

·不能包含空格

·尽量不要包含特殊字符

·区分大小写

XML文件的约束(了解)

为什么要有XML约束

XML本身是可扩展的,用户可以写任意的标签,但是这样一来就可能乱套了,用户的随意会让XML表示的含义不再统一并且解析会变得困难,所以一个框架不应该让用户任意定义标签。我们需要有约束来规范XML可以出现哪些标签,不能出现哪些标签以及标签之间的顺序、出现的次数,标签有哪些属性等。

我们编写XML文件的时候有约束的话还可以根据约束给我们提示该怎么写

XML文件约束的分类

xml 约束分两种: dtd 约束,schama 约束

什么是DTD

DTD(Document Type Definition),文档类型定义,用来约束XML文档。规定XML文档中元素的名称,子元素的名称及顺序,元素的属性等。

一个xml文档中只能添加一个DTD约束

DTD约束的语法

元素声明

定义元素语法:<!ELEMENT 元素名 元素描述>

元素名:自定义
元素描述包括:符号和数据类型

常见符号:? * + () | ,
常见类型:#PCDATA 表示内容是文档,不能是子标签
1.png

DTD用法

2.png

学习了dtd 约束 ,为何要还学习schema约束呢?

因为dtd约束有局限性 :一个xml文档只能有一个dtd约束

什么是Schema

Schema是新的XML文档约束;

Schema要比DTD强大很多,是DTD 替代者;

Schema本身也是XML文档,但Schema文档的扩展名为xsd,而不是xml。

Schema 功能更强大,数据类型更完善

一个xml文档中可以添加多个schema约束

为何支持多个schema 约束 就是因为 Schema 支持名称空间

什么是命名空间

如果一个XML文档中使用多个Schema文件,而这些Schema文件中定义了相同名称的元素时就会出现名字冲突。这就像一个Java文件中使用了import java.util.和import java.sql.时,在使用Date类时,那么就不明确Date是哪个包下的Date了。

总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。

Schema语法

3.png

Schema用法

4.png

XML解析

XML文档结构分析

5.png

开发中比较常见的解析方式

有两种,如下:

  • A. DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象。

    1. 优点:元素与元素之间保留结构关系,故可以进行增删改查操作。

    2. 缺点:XML文档过大,可能出现内存溢出显现。

  • B. SAX:是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都将触发对应的事件。(了解)

    1. 优点:处理速度快,可以处理大文件

    2. 缺点:只能读,逐行后将释放资源。

获取Document对象

要操作XML,先就得有Document对象:

把一个XML文件加载进内存的时候,在内存中形成所谓的一种树状结构,我们把这一个结构称之dom树,创建的对象叫Document对象.

注意:

我们在Java代码中所做的增/删/改/查操作,都仅仅是操作的是内存中的Document对象,和磁盘中的XML文件没有关系.如图:

6.png

比如:删除一个联系人信息之后,XML文件中数据依然存在,此时出现内存中的数据和磁盘文件中的数据不同步,所以,对于增删改操作,我们需要做同步操作(把内存中的数据和磁盘的XML文件数据保持一致).

DOM:在第一次的时候就会把XML文件加载进内存,如果XML文件过大,可能会造成内存的溢出.

DOM:在做增删改查操作的时候比较简单,但是性能却不高(线性搜索).

// 首先要获取Document对象,如何获取呢?

前提是必须有 xml文件

File file = new File("C:/stsworkspace/day03/contacts.xml");

注意:
当导入类/接口的时候,导入的包是org.w3c.dom ,因为xml遵循的是w3c组织的规范

思路:

1.获取一个DocumentBuilderFactory工厂对象

通过DocumentBuilderFactory调用newInstance 方法来获取工厂对象  DocumentBuilderFactory.newInstance();

2.获取DocumentBuilder对象

通过工厂对象调用newDocumentBuilder方法来获取  :工厂对象.newDocumentBuilder() 

3.通过documentBuilder对象,获取document对象

获取的方式有两种:当xml文件不存在的时候,可以调用newDocument方法来创建一个Document对象
当xml文件存在的情况,可以调用parse方法来从xml文件中解析得到一个Document对象

如图:


7.png

DOM中常用的API

Node接口:

String getTextContent():获取元素的文本内容
void setTextContent(String textContent):给元素设置新的文本内容
Node appendChild(Node newChild):往当前元素中增加新的儿子.
老爸.appendChild(新的儿子);

Node removeChild(Node oldChild):从当前节点下删除指定的儿子节点.

Document接口:

    Element getDocumentElement():获取并返回文档的根元素对象.
    Element getElementById(String elementId):根据指定的id属性的值,获取对应的Element对象.
    
    若当前解析的XML文件没有约束文件,并且没有对id属性有ID类型约束,则不能使用该方法.
    NodeList getElementsByTagName(String tagName):根据指定的标签名获取文档中所有的子元素.
    Element createElement(String tagName):根据指定的元素名,创建Element对象.

Element接口:

    NodeList getElementsByTagName(String tagName):根据指定的标签名获取当前元素下(this)所有的子元素.
    void setAttribute(String attrName, String attrValue):为当前元素对象设置指定名子的属性和属性值.
    String getAttribute(String attrName):获取当前元素指定属性名的属性值.

DOM操作练习

需求:得到某个具体的文本节点的内容:取出第二个联系人的名字

//  需求:获取第二个联系人的姓名
@Test
public void testQuery() throws Exception {
        // 1. 获取document对象
        Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
                .newDocumentBuilder() //获取构建器
                .parse(file);
        // 2. 获取文档中的根元素contacts
        Element root = document.getDocumentElement();
        // 3. 获取contacts根元素下面的第二个子元素 linkman
        Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(1);
        // 4. 获取linkman元素下面的子元素name
        Node nameEle = linkmanEle.getElementsByTagName("name").item(0);
        // 5. 获取name元素对应的文本内容
        String content = nameEle.getTextContent();
        System.out.println(content);
    }
    

需求:修改某个元素节点的主体内容:第一个联系人的姓名为“马云”

//  需求:修改第一个联系人的姓名为“马云”
    @Test
    public void testUpdate() throws Exception {
        // 1. 获取document对象
        Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
                .newDocumentBuilder() //获取构建器
                .parse(file);
        // 2. 获取文档中的根元素contacts
        Element root = document.getDocumentElement();
        // 3. 获取contacts根元素下面的第二个子元素 linkman
        Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(0);
        // 4. 获取linkman元素下面的子元素name
        Node nameEle = linkmanEle.getElementsByTagName("name").item(0);
        //5. 修改name元素对应的文本内容
        nameEle.setTextContent("马云");
        //6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult(file));
    }

如图:

8.png

需求:向指定元素节点中增加子元素节点:增加一个联系人信息

//  需求:往xml中新增一个新的联系人
    @Test
    public void testAdd() throws Exception {
        // 1. 获取document对象
        Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
                .newDocumentBuilder() //获取构建器
                .parse(file);
        // 2. 获取文档中的根元素contacts
        Element root = document.getDocumentElement();
        // 3. 创建元素(linkman,name,address,group)
        Element linkmanEle = document.createElement("linkman");
        Element nameEle = document.createElement("name");
        Element addressEle = document.createElement("address");
        Element groupEle = document.createElement("group");
        // 4.  给上面创建的元素设置文本内容
        linkmanEle.setAttribute("id", "3");
        nameEle.setTextContent("李清照");
        addressEle.setTextContent("上海青浦区");
        groupEle.setTextContent("叩丁狼教育");
        // 5.  把元素name,address ,group 设置到linkman中
        linkmanEle.appendChild(nameEle);
        linkmanEle.appendChild(addressEle);
        linkmanEle.appendChild(groupEle);
        // 6.  把linkman元素设置到contacts中
        root.appendChild(linkmanEle);
        // 7.  同步数据:把内存中的数据同步到磁盘上
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult(file));
    }
    

需求:操作XML文件属性:设置/获取联系人的id属性

//  需求:设置/获取第一个联系人的id属性值为10
    @Test
    public void testDelete() throws Exception {
        // 1. 获取document对象
        Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
                .newDocumentBuilder() //获取构建器
                .parse(file);
        // 2. 获取文档中的根元素contacts
        Element root = document.getDocumentElement();
        // 3. 获取contacts根元素下面的第一个子元素 linkman
        Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(0);
        // 4. 设置属性的值为10
         linkmanEle.setAttribute("id",10);
        // 5. 获取属性的值
         System.out.println("id的值为:" +linkmanEle.getAttribute("id") );  
        //6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult(file));

    }
}

需求:删除指定元素节点:删除第三个联系人信息

//  需求:删除第三个联系人信息
    @Test
    public void testDelete() throws Exception {
        // 1. 获取document对象
        Document document = DocumentBuilderFactory.newInstance()//获取工厂对象
                .newDocumentBuilder() //获取构建器
                .parse(file);
        // 2. 获取文档中的根元素contacts
        Element root = document.getDocumentElement();
        // 3. 获取contacts根元素下面的第三个子元素 linkman
        Element linkmanEle = (Element) root.getElementsByTagName("linkman").item(2);
        // 4. 删除第三个联系人信息
        root.removeChild(linkmanEle);
        //方式二:linkmanEle.getParentNode().removeChild(linkmanEle);
        //6. 同步数据:把内存中的数据同步到磁盘上 使用的核心类: Transformer
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(new DOMSource(document), new StreamResult(file));

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容