天天看点

javaweb 之 XML总结(一)XML

XML

一、简介

XML 指可扩展标记语言(EXtensible Markup Language)。

XML 是一种标记语言,很类似 HTML,也是使用标签来操作。

可扩展:HTML里面的标签是固定的,每个标签都有特定的含义。而XML里面的标签可以自己定义,甚至可以写中文的标签。

XML的用途:HTML是用于显示数据的,XML也是可以显示数据的,但它不是XML的主要功能,XML 的设计宗旨是传输数据,而非显示数据。

XML是两个版本,1.0和1.1。我们一般使用1.0版本,因为1.1版本不能向下兼容。

二、XML的应用

  • 不同系统之间传输数据

    最早的时候,消息发送信息的格式为字符串,比如:

    String str = "zhangsan:lisi:hello:2019-4-19";

    字符串中我们用

    :

    作为分隔,每一段代表一个信息。 但是这样的代码,别人很难明白其中的含义,阅读性很差。 而且如果想扩展一下,添加一些新的信息,就会很困难。所以这种格式可维护性很差。

    现在我们使用的新的方式来表示信息:

    String str = "
    <message id='1'>
    	<sender>zhangsan</sender>
    	<getter>lisi</getter>
    	<content>hello</content>
    	<date>2019-4-19</date>
    </message>
    ";
               
    这样的话,可阅读性大大提高,而且扩展起来很方便,大大提高了程序的可维护性。
  • 用来表示生活中有关系的数据

    生活中的事物之间存在大量的关系,我们可以使用xml来表示这些有关系的数据。

    例如:

    javaweb 之 XML总结(一)XML
  • 经常用在配置文件

    例如在java连接数据库的时候,需要数据库的URL、用户名和密码,我们把它存在xml中,如果想要修改,就不许需要修改源代码,便于修改和维护。

三、xml的语法

xml的文档说明

  • 创建一个文件,后缀名是 .xml 。
  • 如果写xml,第一步必须要有一个文档说明

    <?xml version="1.0" encoding="gbk"?>

  • 文档说明必须写在第一行第一列
  • 属性:

    version:xml的版本

    encoding:xml编码、gbk、utf-8、iso8859-1(不包含中文)

    standalone:是否需要依赖其他的文件 yes/no

如果 xml中文乱码问题,在保存 xml文件的时候把编码设置成跟文档声明一样的类型。

定义元素(标签)

标签的定义:

  • 标签定义必须有开始有结束:

    <person> </person>

  • 标签没有内容,可以在标签内结束:

    <aa/>

  • 标签可以嵌套,但是必须合理嵌套

    合理嵌套:

    <aa> <bb></bb> </aa>

    不合理嵌套:

    <aa> <bb></aa> </bb>

    不正确!
  • 一个xml中只能有一个根标签!其他的标签都是这个标签下面的标签。
  • 注意:在xml中,空格和换行都当成内容来解析,下面这两段代码的含义是不同的。
    <aa>
    	hello
    </aa>
               
  • xml中标签的命名规则
    1. xml代码区分大小写,

      <p>

      <P>

      这两个标签是不同的。
    2. 标签名称不能以

      数字

      或者

      下划线

      开头。

      <2a>

      <_aa>

      都不可允许。
    3. 不能以

      xml

      XML

      Xml

      等开头。
    4. 标签里面不能包含空格和冒号。

      a b

      b:c

      都是不允许的。
    5. 标签可以使用中文,但是不建议这样做。

定义属性

html是标记型文档,可以有属性。

xml也是标记型文档,也可以有属性。

属性定义的要求:

  1. 一个标签上可以有多个属性

    <person id1="aaa" id2="bbb"></person>

  2. 属性名称不能相同。
  3. 属性名称与属性值之间用

    =

    连接,属性值要用引号括起来,可以是单引号,可以是双引号。
  4. 属性的命名规范和元素的命名规范是一致的。

注释

写法:

<!-- xml的注释 -->

注意:注释是不能有嵌套的。

注释也不能放到第一行!

特殊字符

<a>a<b</a>

如果想在xml中显示 a<b ,是不能正常显示的,因为把<当成标签。

如果想要显示,就需要使用转义字符。

常用的转义字符:

&lt;

< 小于

&gt;

> 大于

&amp;

& 和号

&apos;

’ 单引号

&quot;

" 引号

CDATA区

如果我们想要输入:

我们知道是显示不出来的,因为里面的小于号都需要转义。

而CDATA区可以解决多个字符都需要的转义的操作。

写法:

<![CDATA[ 内容 ]]>

上面的我们要修改为:

<![CDATA[ <aa>if(a<b && b<c && c<d){}</aa> ]]>
           

这样把特殊字符当做文本内容,而不是标签。

PI指令(处理指令)

可以在xml中设置样式。可以通过PI指令来引入外部css文件。

写法:

<?xml-stylesheet type="text/css" href="xxx.css" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" ?>

示例:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="xxx.css" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" ?>
<person>
	<name>zhangsan</name>
	<age>20</age>
</person>
           

注意:设置样式,只对于英文名称的标签起作用,对于中文名称的标签是不起作用的。

四、xml的约束

  • 为什么需要约束?
    <?xml version="1.0" encoding="utf-8"?>
    <person>
    	<name>zhangsan</name>
    	<age>20</age>
    </person>
               
    比如我们现在定义一个person的xml文件,只想要在里面保存人的一些信息,比如name、age等,但是如果在xml文件中写入了一个

    <猫>

    的标签,发现它是可以正常显示的,因为符合语法规范。但是猫肯定不是人的信息,所以我们这个时候就需要一种技术来规定xml中只能出现的元素,这就叫xml的约束。
  • xml中约束的技术:dtd约束和schema约束

dtd约束

我们创建一个文件,后缀名是

.dtd

步骤:

  1. 看xml中有多少元素,有几个元素,在dtd文件中写几个

    <!ELEMENT>

  2. 判断这个元素是简单元素还是复杂元素。

    复杂元素: 有子元素的元素

    格式:

    <!ELEMENT 元素名称 (子元素)>

    例如上面的person:

    <!ELEMENT person (name,age)>

    简单元素: 没有子元素

    格式:

    <!ELEMENT 元素名称 (#PCDATA)>

    例如上面的age、name:

    <!ELEMENT name (#PCDATA)>

    <!ELEMENT age (#PCDATA)>

  3. 需要在xml文件中引入dtd文件。

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

如果使用浏览器打开 xml文件的话,浏览器只负责校验 xml的语法,不负责校验 xml的约束。

如果想要校验 xml的约束,需要使用工具,工具有很多种,例如 MyEclipse。

javaweb 之 XML总结(一)XML

dtd的三种引入方式

  • 引入外部的dtd文件

    <!DOCTYPE 根元素 SYSTEM "dtd路径">

  • 内部的dtd
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE person [
    	<!ELEMENT person (name,age)>
    	<!ELEMENT name (#PCDATA)>
    	<!ELEMENT age (#PCDATA)>
    ]>
    <person>
    	<name>zhangsan</name>
    	<age>20</age>
    </person>
               
  • 使用外部的dtd文件(网络上的dtd文件)
    <!DOCTYPE 根元素 PUBLIC "DTD名称" "dtd文档的URL">
               
    在structs2 使用配置文件 使用 外部的dtd文件,需要用到这个知识。

dtd定义元素

语法:

<!ELEMENT 元素名称 约束>

  • 简单元素:

    <!ELEMENT name (#PCDATA)>

    (#PCDATA)

    :约束name是字符串类型

    EMPTY

    :元素为空,不能有内容。

    ANY

    :任意,可以为空,也可以不为空
  • 复杂元素:

    <!ELEMENT 元素名称 (子元素)>

    例如:

    <!ELEMENT person (name,age)>

    这样的格式下,name和age子元素只能出现一次

    设置子元素出现的次数:

    +

    :出现最少一次,一次或者多次

    ?

    :最多出现一次,零次或者一次

    *

    :任意次数都可以,零次、一次、多次都可以

    示例:

    <!ELEMENT person (name+,age?)>

    子元素用

    ,

    隔开,也表示元素出现的顺序,必须按照先后顺序出现。

    子元素用

    |

    隔开,表示只能出现其中的任意一个。

dtd定义属性

使用dtd约束标签上的属性,语法:

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

属性类型

  • 字符串类型:CDATA
    <!ATTLIST person
    	name CDATA #REQUIRED
    >
               
  • 枚举类型:表示一定范围内只能出现其中一个。
    <!ATTLIST person
    	name (AA|BB|CC) #REQUIRED
    >
               
  • ID 表示属性的取值不能重复,而且必须以字母或者下划线开头。
    <!ATTLIST person
    	name ID #REQUIRED
    >
               

    属性的约束

    #REQUIRED:属性必须存在

    #REQUIRED

    #IMPLIED:属性可有可无

    #IMPLIED

    #FIXED:表示一个固定值

    #FIXED "AAA"

    直接值:表示属性默认值,当在标签上不写这个属性的时候,就使用直接值,如果写了属性,就使用写的值。

dtd定义引用实体

概念:在dtd中定义,在xml中使用。

语法:

<!ENTITY 实体名称 "实体内容">

引用方式:

&实体名称;

例如:

dtd中定义:

<!ENTITY copyright "veeja版权所有">

xml中引用:

&copyright;

定义的实体需要使用在内部的dtd中,如果写在外部的dtd中,在某些版本的浏览器,是不能正常使用的。

五、XML如何解析

DOM和SAX

xml有两种解析方式:dom和sax。

  • DOM解析XML:

    与DOM解析HTML类似的是,dom解析的时候也会根据xml的层级结构,把xml中的每部分封装成一个对象,在内存中分配一个树形结构。

    DOM的优点在于可以很方便的实现增删改的操作。

    DOM的缺点在于解析xml文件的时候,如果xml文件过大,就会导致内存溢出。

  • sax解析xml:

    sax解析采用事件驱动,一边读取一边解析,解析到某一个对象,把对象的名称返回。

    使用sax方式不会造成内存溢出,也可以实现查询的操作。

    但是使用sax方式,不能实现增删改的操作。

解析器

想要解析xml,首先需要解析器。

不同的公司和组织提供了针对dom和sax方式的解析器,通过api方式提供。

  • SUN公司提供了针对dom和sax的解析器,叫 jaxp。
  • dom4j组织,针对dom和sax解析,提供了 dom4j。这个解析器实际开发中应用的最广泛。
  • jdom组织,针对dom和sax解析,提供了 jdom。

六、JAXP

JAXP:Java API for XMLProcessing,意为XML处理的Java API。它是JavaSE的一部分。

jaxp解析器在jdk的javax.xml.parsers包里面。

里面有四个类,分别是针对dom和sax解析使用的类。

针对DOM

DocumentBuilder:解析器类

这个类是一个抽象类,不能new。

但是可以使用

DocumentBuilderFactory.newDocumentBuilder()

方法获取实例。

可以使用方法来解析xml:

parse(xml的路径)

,返回的是一个Document接口。

方法:

Document是一个接口,父接口是Node,如果在Document中找不到相应的方法,就去Node里面去找。

Document里面的

getElementsByTagName(String tagname)

方法 可以得到标签,返回的是一个集合NodeList。

createElement(String tagName)

:创建标签

createTextNode(String data)

:创建文本

appendChild(Node newChild)

:将节点 newChild 添加到此节点的子节点列表的末尾。

removeChild(Node oldChild)

:从子节点列表中移除 oldChild 所指示的子节点,并将其返回。

replaceChild(Node newChild, Node oldChild)

:将子节点列表中的子节点 oldChild 替换为 newChild,并返回 oldChild 节点。

getParentNode()

:得到此节点的父节点。

getFirstChild()

:此节点的第一个子节点。

getLastChild()

:此节点的最后一个节点。

这些方法不存在兼容性问题,可以放心使用。

前面得到的NodeList对象,可以使用

getLength()

获取列表节点数,使用

item(int index)

返回集合中的第 index 个项。

例如:

NodeList list

for(int i = 0; i < list.getLength(); i++){
	list.item(i);
}
           

DocumentBuilderFactory:解析器工厂

这个类也是一个抽象类,不能new,可以通过

newInstance()

方法获取 DocumentBuilderFactory 的实例。

针对SAX

SAXParser:解析器类

SAXParserFactory:解析器工厂

使用JAXP实现查询操作

  • 创建一个xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<person>
	<p1>
		<name>weijia</name>
		<age>21</age>
	</p1>
	<p2>
		<name>lingjie</name>
		<name>22</name>
	</p2>
</person>
           
  • 查询xml中所有的name元素的值。
    1. 创建解析器工厂类
    2. 根据解析器工厂类创建解析器
    3. 解析xml返回Document
    4. 得到所有的name元素
    5. 返回集合,遍历集合,得到每一个name元素
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document document = db.parse("src/person.xml");
    NodeList nl = document.getElementsByTagName("name");
    for (int i = 0; i < nl.getLength(); i++) {
    	Node name1 = nl.item(i);
    	System.out.println(name1.getTextContent());
    }
               

使用JAXP实现查询某一个节点

查询xml中第一个name元素的值

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document d = db.parse("src/person.xml");
NodeList nl = d.getElementsByTagName("name");
Node node = nl.item(0);
System.out.println(node.getTextContent());
           

使用JAXP添加一个节点

我们p1标签添加一个子标签

<sex>

,并且标签内容设置为“女”。

public static void addSex() throws Exception {
	// 创建解析器工厂
	DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	// 创建解析器
	DocumentBuilder db = dbf.newDocumentBuilder();
	// 解析xml,得到document
	Document d = db.parse("src/person.xml");
	// 得到所有的p1
	NodeList eles = d.getElementsByTagName("p1");
	// 得到第一个p1
	Node ele = eles.item(0);
	// 创建sex标签
	Element newElement = d.createElement("sex");
	// 创建文本元素,内容为nv
	Text text = d.createTextNode("nv");
	// 把文本元素放入sex标签
	newElement.appendChild(text);
	// 把sex标签放入第一个p1标签
	ele.appendChild(newElement);

	// 回写xml文件
	TransformerFactory factory = TransformerFactory.newInstance();
	Transformer transformer = factory.newTransformer();
	transformer.transform(new DOMSource(d), new StreamResult("src/person.xml"));
}
           

使用JAXP修改节点

把p1标签的sex标签内容设置为“nan”。

public static void modSex() throws Exception {
	// 创建解析器工厂
	DocumentBuilderFactory dbt = DocumentBuilderFactory.newInstance();
	// 创建解析器
	DocumentBuilder db = dbt.newDocumentBuilder();
	// 解析xml,得到document
	Document document = db.parse("src/person.xml");

	// 得到sex1
	Node sex1 = document.getElementsByTagName("sex").item(0);
	// 设置为nan
	sex1.setTextContent("man");

	// 回写xml文件
	TransformerFactory factory = TransformerFactory.newInstance();
	Transformer transformer = factory.newTransformer();
	transformer.transform(new DOMSource(document), new StreamResult("src/person.xml"));
}
           

使用JAXP删除节点

把sex标签删除掉。

public static void deleteSex() throws Exception {
	// 创建解析器工厂
	DocumentBuilderFactory dbt = DocumentBuilderFactory.newInstance();
	// 创建解析器
	DocumentBuilder db = dbt.newDocumentBuilder();
	// 解析xml,得到document
	Document document = db.parse("src/person.xml");

	// 得到sex标签
	NodeList sex = document.getElementsByTagName("sex");
	// 判断是否有这个标签
	if (sex.getLength() != 0) {
		// 得到第一个sex标签
		Node sex1 = sex.item(0);
		// 得到sex1的父标签,也就是p1标签
		Node p1 = sex1.getParentNode();
		// 删除sex1标签
		p1.removeChild(sex1);

		// 回写xml文件
		TransformerFactory factory = TransformerFactory.newInstance();
		Transformer transformer = factory.newTransformer();
		transformer.transform(new DOMSource(document), new StreamResult(
				"src/person.xml"));
	}
}
           

使用JAXP遍历节点

把xml中所有的元素名称打印出来。

使用递归来实现:

public static void list() throws Exception {
	/*
	 * 1. 创建解析器工厂类 2. 根据解析器工厂类创建工厂类 3. 解析xml,返回document
	 */
	DocumentBuilderFactory builderFactory = DocumentBuilderFactory
			.newInstance();
	DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
	Document document = documentBuilder.parse("src/person.xml");

	Node firstChild = document.getFirstChild();
	/*
	 * ====使用递归来实现==== 得到根节点 得到根节点的子节点 得到子节点的子节点
	 */
	list1(firstChild);
}

private static void list1(Node node) {
	// 判断一下是不是标签,如果是空格和换行的话,就跳过
	if (node.getNodeType() == Node.ELEMENT_NODE) {
		System.out.print("<");
		System.out.print(node.getNodeName());
		System.out.println(">");
	}
	NodeList list = node.getChildNodes();
	for (int i = 0; i < list.getLength(); i++) {
		Node node1 = list.item(i);
		list1(node1);
	}
}