XML的jaxp解析和schema约束

一、xml的解析的简介

* xml是标记型文档
* js使用dom解析标记型文档?
- 根据html的层级结构,在内存中分配一个树形结构,把html的标签,属性和文本都封装成对象
- document对象、element对象、属性对象、文本对象、Node节点对象

* xml的解析方式(技术):dom 和 sax
*** dom解析和sax解析区别:
** dom方式解析
* 根据xml的层级结构在内存中分配一个树形结构,把xml的标签,属性和文本都封装成对象
* 缺点:如果文件过大,造成内存溢出
* 优点:很方便实现增删改操作

** sax方式解析
* 采用事件驱动,边读边解析
- 从上到下,一行一行的解析,解析到某一个对象,返回对象名称
* 缺点:不能实现增删改操作
* 优点:如果文件过大,不会造成内存溢出,方便实现查询操作

* 想要解析xml,首先需要解析器
** 不同的公司和组织提供了 针对dom和sax方式的解析器,通过api方式提供
*** sun公司提供了针对dom和sax解析器  jaxp
*** dom4j组织,针对dom和sax解析器    dom4j(*** 实际开发中****)
*** jdom组织,针对dom和sax解析器     jdom


二、jaxp

** jaxp是javase的一部分

** jaxp解析器在jdk的javax.xml.parsers包里面
** 四个类:分别是针对dom和sax解析使用的类
*** dom: 
DocumentBuilder  : 解析器类
- 这个类是一个抽象类,不能new,
此类的实例可以从 DocumentBuilderFactory.newDocumentBuilder() 方法获取


- xml  parse("xml路径") 方法,可以解析xml,返回是 Document 整个文档
- 返回的document是一个接口,父接口是Node,如果在document里面找不到想要的方法,到Node里面去找

- Document接口
getElementsByTagName(String tagname) 
-- 这个方法可以得到标签
-- 返回集合 NodeList

createElement(String tagName)
-- 创建标签

createTextNode(String data) 
-- 创建文本

appendChild(Node newChild) 
-- 把文本添加到标签下面

removeChild(Node oldChild) 
-- 删除节点

getParentNode() 
-- 获取父节点

NodeList list接口

- getLength() 得到集合的长度
- item(int index)下标取到具体的值
for(int i=0;i<list.getLength();i++) {
list.item(i);      //返回Node接口
}

                                        Node接口

getTextContent()
- 得到标签里面的内容

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

newInstance() 获取 DocumentBuilderFactory 的实例。

三、使用jaxp实现查询操作

*** 查询xml中所有的name元素的值
* 步骤
//查询所有name元素的值
/*
* 1、创建解析器工厂
DocumentBuilderFactory.newInstance();
* 2、根据解析器工厂创建解析器
builderFactory.newDocumentBuilder();
* 3、解析xml返回document
* Document document = builder.parse("src/Person.xml");
* 4、得到所有的name元素
使用document.getElementsByTagName("name");
* 5、返回集合,遍历集合,得到每一个name元素
- 遍历 getLength() item()
- 得到元素里面值 使用 getTextContent()

* */

DocumentBuilderFactory dbf= DocumentBuilderFactory.newInstance();
DocumentBuilder db=dbf.newDocumentBuilder();
Document d=db.parse("src/Person.xml");
NodeList nl=d.getElementsByTagName("name");
for(int i=0;i<nl.getLength();i++){
    Node n=nl.item(i);
    System.out.println(n.getTextContent());
}

四、使用jaxp添加节点

*** 在第student下面(末尾)添加 <sex>男</sex>
**步骤
/*
* 1、创建解析器工厂
* 2、根据解析器工厂创建解析器
* 3、解析xml,返回document
* 4、得到所有student
                            使用item方法下标得到
* 5、创建sex标签 createElement
* 6、创建文本 createTextNode
* 7、把文本添加到sex下面 appendChild
* 8、把sex添加到第一个p1下面 appendChild
* 9、回写xml

* */

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document document = builder.parse("src/Person.xml");
NodeList nodeList = document.getElementsByTagName("student");
for (int i = 0; i < nodeList.getLength(); i++) {
	Node node = nodeList.item(i);
	Node node2 = document.createElement("sex");
	Node node3 = document.createTextNode("男");
	node2.appendChild(node3);
	node.appendChild(node2);
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new DOMSource(document), new StreamResult("src/Person.xml"));

回写xml需要用到Transformer抽象类,该类需要通过TransformerFactory类的newTransformer() 方法来获取。

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();

用于回写的方法:

        transform(Source xmlSource, Result outputTarget)

                    参数:Source xmlSource:要转换的xml输入  (该类的参数为Node接口)

                            outputTarget:转换 xmlSource 的 Result   (该类的参数写入的路径)

五、使用jaxp修改节点

*** 修改第一个p1下面的sex内容是nan
** 步骤
/*
* 1、创建解析器工厂
* 2、根据解析器工厂创建解析器
* 3、解析xml,返回document 

* 4、得到sex item方法
* 5、修改sex里面的值  
*** setTextContent方法

* 6、回写xml

* */

六、使用jaxp删除节点

*** 删除<sex>nan</sex>节点
** 步骤
/*
* 1、创建解析器工厂
* 2、根据解析器工厂创建解析器
* 3、解析xml,返回document 

* 4、获取sex元素
* 5、获取sex的父节点  使用getParentNode方法
* 6、删除使用父节点删除  removeChild方法

* 7、回写xml

* */

七、使用jaxp遍历节点

** 把xml中的所有元素名称打印出来
** 步骤
/*
* 1、创建解析器工厂
* 2、根据解析器工厂创建解析器
* 3、解析xml,返回document

* ====使用递归实现=====
* 4、得到根节点
* 5、得到根节点子节点
* 6、得到根节点子节点的子节点

* */

public static void main(String[] args) throws Exception {
	DocumentBuilderFactory builderFactory = DocumentBuilderFactory
			.newInstance();
	DocumentBuilder builder = builderFactory.newDocumentBuilder();
	Document document = builder.parse("src/Person.xml");
	list(document);
}

private static void list(Node node) {
	//判断是元素类型时才打印
	if (node.getNodeType() == Node.ELEMENT_NODE) {
		System.out.println(node.getNodeName());
	}
	//得到该节点的子节点集合
	NodeList list = node.getChildNodes();
	for (int i = 0; i < list.getLength(); i++) {
		//得到每一个子节点
		Node node1 = list.item(i);
		//递归调用
		list(node1);
	}
}

八、sax解析的原理

* 根据xml的层级结构在内存中分配一个树形结构
** 把xml中标签,属性,文本封装成对象

* sax方式:事件驱动,边读边解析
* 在javax.xml.parsers包里面
** SAXParser
此类的实例可以从 SAXParserFactory.newSAXParser() 方法获得
- parse(File f, DefaultHandler dh) 
* 两个参数
    ** 第一个参数:xml的路径
    ** 事件处理器
** SAXParserFactory
实例 newInstance() 方法得到
* sax执行过程

* 当解析到开始标签时候,自动执行startElement方法

                        startElement(String uri, String localName, String qName, Attributes attributes)

* 当解析到文本时候,自动执行characters方法

                        characters(char[] ch, int start, int length)

* 当解析到结束标签时候,自动执行endElement方法

                        endElement(String uri, String localName, String qName) 

九、使用jaxp的sax方式解析xml

* sax方式不能实现增删改操作,只能做查询操作
** 打印出整个文档
*** 执行parse方法,第一个参数xml路径,第二个参数是 事件处理器
*** 创建一个类,继承事件处理器的类,
***重写里面的三个方法

* 获取到所有的name元素的值
** 定义一个成员变量 flag= false
** 判断开始方法是否是name元素,如果是name元素,把flag值设置成true
** 如果flag值是true,在characters方法里面打印内容
** 当执行到结束方法时候,把flag值设置成false

* 获取第一个name元素的值
** 定义一个成员变量 idx=1
** 在结束方法时候,idx+1 idx++
** 想要打印出第一个name元素的值,
- 在characters方法里面判断,
-- 判断flag=true 并且 idx==1,在打印内容
public class SaxDemo {
	public static void main(String[] args) throws Exception {
		SAXParserFactory parserFactory = SAXParserFactory.newInstance();
		SAXParser saxParser = parserFactory.newSAXParser();
		MyDefault dh = new MyDefault();
		saxParser.parse("src/Person.xml", dh);
	}
}

// 创建一个类,继承事件处理器的类
// 重写里面的三个方法
class MyDefault extends DefaultHandler {
	// 定义一个成员变量 boo= false
	boolean boo = false;

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		// 判断开始方法是否是name元素,如果是name元素,把boo值设置成true
		if (qName.equals("name")) {
			boo = true;
			System.out.print("<" + qName + ">");
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		// 当执行到结束方法时候,把flag值设置成false
		if (qName.equals("name")) {
			boo = false;
			System.out.println("</" + qName + ">");
		}
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		// 如果flag值是true,在characters方法里面打印内容
		if (boo) {
			System.out.print(new String(ch, start, length));
		}
	}
}


十、schema约束

dtd语法: <!ELEMENT 元素名称 约束>
** schema符合xml的语法,xml语句
** 一个xml中可以有多个schema,多个schema使用名称空间区分(类似于java包名)
** dtd里面有PCDATA类型,但是在schema里面可以支持更多的数据类型
*** 比如 年龄 只能是整数,在schema可以直接定义一个整数类型
*** schema语法更加复杂,schema目前不能替代dtd

十一、schema的快速入门

* 创建一个schema文件 后缀名是 .xsd
** 根节点 <schema>
** 在schema文件里面
** 属性  xmlns="http://www.w3.org/2001/XMLSchema"
- 表示当前xml文件是一个约束文件
** targetNamespace="http://www.example.org/1" 
- 使用schema约束文件,直接通过这个地址引入约束文件
** elementFormDefault="qualified"
步骤
(1)看xml中有多少个元素
<element>
(2)看简单元素和复杂元素
* 如果复杂元素
<complexType>
<sequence>
子元素
</sequence>
</complexType>
(3)简单元素,写在复杂元素的里面
<element name="person">
<complexType>
<sequence>
<element name="name" type="string"></element>
<element name="age" type="int"></element>
</sequence>
</complexType>
</element>

(4)在被约束文件里面引入约束文件
<person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.itcast.cn/20151111"
xsi:schemaLocation="http://www.example.org/1 1.xsd" >

** xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-- 表示xml是一个被约束文件
** xmlns="http://www.itcast.cn/20151111"
-- 是约束文档里面 targetNamespace
** xsi:schemaLocation="http://www.itcast.cn/20151111 1.xsd">
-- targetNamespace 空格  约束文档的地址路径

* <sequence>:表示元素的出现的顺序
   <all>: 元素只能出现一次
   <choice>:元素只能出现其中的一个
    maxOccurs="unbounded": 表示元素的出现的次数
   <any></any>:表示任意元素

* 可以约束属性
* 写在复杂元素里面
***写在 </complexType>之前
--
<attribute name="id1" type="int" use="required"></attribute>
- name: 属性名称
- type:属性类型 int string
- use:属性是否必须出现 required

* 复杂的schema约束
        * 引入多个schema文件,可以给每个起一个别名

<?xml version="1.0" encoding="UTF-8"?>
<!-- 数据文件 引用多个Schema -->
<company xmlns = "http://www.example.org/company"
	xmlns:dept="http://www.example.org/department"
	xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.example.org/company company.xsd http://www.example.org/department department.xsd" 
>
	<employee age="30">
		<!-- 部门名称 --> 
		<dept:name>人力资源部</dept:name>
		<!-- 员工名称 -->
		<name>王晓晓</name>   
	
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" 
targetNamespace="http://www.example.org/company" 
elementFormDefault="qualified">
	<element name="company">
		<complexType>
			<sequence>
				<element name="employee">
					<complexType>
						<sequence>
							<!-- 引用任何一个元素 -->
							<any></any>
							<!-- 员工名称 -->
							<element name="name"></element>
						</sequence>
						<!-- 为employee元素添加属性 -->
						<attribute name="age" type="int"></attribute>
					</complexType>
				</element>
			</sequence>
		</complexType>
	</element>
</schema>
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
 targetNamespace="http://www.example.org/department" 
 elementFormDefault="qualified">
 <!-- 部门名称 -->
 <element name="name" type="string"></element>
</schema>


猜你喜欢

转载自blog.csdn.net/fy_java1995/article/details/80050658