SAX解析是基于事件编程的
返回事件编程的三要素:
1.事件源:要操作指定的xml文件
2.事件监听器:
- 遇到文档触发方法
startDocument()
- 遇到开始标签触发方法
startElement()
- 遇到文本内容触发方法
characters()
- 遇到结束标签触发的方法
endElement()
- 遇到文档结束触发方法
endDocument()
3.注册事件监听器:
开发步骤:
- 1.创建SAXParser解析器对象,SAXParser是一个抽象类,他的实例需要通过工厂方法实现
SAXParser parser=SAXParserFactory.newInstance().newSAXParser();
//多态 - 2.解析:定义 类 extends DefaultHandler,覆写方法,在事件监听器中实现事件处理逻辑
- 3.注册监听事件
parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("contact.xml"),DefaultHandler对象);
SAX解析特点:
读一点取一点,节省空间,一旦遇到开始标签触发之后,及时从内存中将开始节点从内存中释放
SAX解析和DOM解析的区别
SAX | DOM | |
---|---|---|
原理 | 读取一点解析一点,节省内存空间 | 一次性加载整个xml文件,如果xml文件结构层次深OOM异常,浪费空间 |
使用过程 | 只能读 | 不仅能读还能修改,并且能往返读 |
编程思想 | 面向事件编程 | 面向对象编程 |
应用:解析contact.xml,遇到xml标签时,将Contact联系人对象
开发步骤:
- 1.构造List
- 2.遇到contact开始标签,创建一个联系人对象,封装属性
- 3.遇到结束标签,读完之后,将contact 联系人对象添加到List
- 4.遍历集合
public class MySAXDemo2 {
/*SAX解析
* 解析contact.xml,遇到xml标签时,将Contact联系人对象
*
* * */
public static void main(String[] args) throws Exception {
//创建SAXParser解析器对象,SAXParser是一个抽象类,他的实例需要通过工厂方法实现
SAXParser parser=SAXParserFactory.newInstance().newSAXParser();//多态
//解析
//注册监听事件
MyDefaultHandler2 handler2=new MyDefaultHandler2();
parser.parse(MySAX.class.getResourceAsStream("contact.xml"),handler2);
}
}
事件处理程序:
/*
* 事件处理程序
* 逻辑需要自己定义
*
* */
public class MyDefaultHandler2 extends DefaultHandler {
List<Contact> list = new ArrayList<Contact>();
private Contact contact = null;
private String curTag;
public List<Contact> getList() {
return list;
}
/*
* 文档开始时 触发此方法
* */
@Override
public void startDocument() throws SAXException {
}
/**
* @Description: 当遇见开始标签时触发方法
* @Param: qName:开始标签名称
* attributes 属性列表
* @return:
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
curTag = qName;
//如何判断读到的是开始标签
if (qName.equals("contact")) {
contact = new Contact();
String id = attributes.getValue("id");
contact.setId(id);
}
}
/**
* @Description: 当遇到标签的文本触发
* @Param: ch:字符数组
* start:从某个位置开始获取文本
* length:指定长度:实际长度
* @return:
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//遇到文本,每次读取的都是当前文本的实际内容
String content=new String(ch,start,length);
//判断curTag是name 还是phone...
if("name".equals(curTag)){
contact.setName(content);
}
if("phone".equals(curTag)){
contact.setPhone(content);
}
if("email".equals(curTag)){
contact.setEmail(content);
}
if("address".equals(curTag)){
contact.setAddress(content);
}
if("gender".equals(curTag)){
contact.setGender(content);
}
}
/**
* @Description: qName:结束标签名称
* @Param:
* @return:
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if(qName.equals("contact")){
System.out.println(contact);
}
}
}
运行时我遇到了这样的错误
Exception in thread "main" java.lang.IllegalArgumentException: InputStream cannot be null
at javax.xml.parsers.SAXParser.parse(SAXParser.java:191)
at www.github.sweeeeeet.sax.MySAX.main(MySAX.java:24)
我意识到这样的获取类加载器的方式是否有误,因为java天生是多线程的,因此需改为采用获取绝对classpath的方式,于是我将main方法中获取xml文件的方式改为:parser.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("contact.xml"),handler2);
,于是就运行成功。
XML文件的约束
XML文件的DTD约束:开发者对xml文件制定的规则,分为内部约束方式和外部约束方式:
1.外部方式:
note.xml:
<?xml version="1.0"?>
<!--
xml文件的DTD约束:
内部文件约束的方式
-->
<!--外部方式:
<!DOCTYPE 根元素 SYSTEM "文件名(约束文件)">
-->
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body id="002">Don't forget me this weekend</body>
<body id="002">Don't forget me this weekend</body>
</note>
note.dtd:
<!ELEMENT note (to?,from*,heading,body+)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
<!--
<!ATTLIST 元素名称 属性名称 属性类型 默认值>
-->
<!--body id CDATA:约束body的内容是普通字符串并且必须指定id属性-->
<!ATTLIST body id CDATA #FIXED "002">
schema约束:
schema约束文件后缀名:.xsd
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.bite.cn"
elementFormDefault="qualified">
<xs:element name='书架' >
<xs:complexType>
<xs:sequence maxOccurs='unbounded' >
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:integer' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:string' />
</xs:sequence>
<xs:attribute name="id" type="xs:integer"></xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<bite:书架 xmlns:bite="http://www.bite.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bite.cn book.xsd">
<bite:书 id="1">
<bite:书名>10</bite:书名>
<bite:作者>张三</bite:作者>
<bite:售价>28.00元</bite:售价>
</bite:书>
</bite:书架>
格式:名称空间:定义xmlns:名称空间=“别名”(url)
使用名称空间在xml文件中定义标签时,必须要指定别名“http://www…”
关联当前别名地址:schemaLocation=“别名1 地址1 别名2 地址2…”
定义后的名称空间指定标签名称格式:<名称空间:标签名称>
,表示该标签被对应的名称空间约束,具体的约束写在了名称空间关联的别名(url地址)对应的.xsd文件中。