Sax解析xml_动力节点Java学院整理

时间:2022-08-26 21:30:06

JAVA 解析 XML 通常有两种方式,DOM 和 SAX。DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一个驻留内存的树结构(节点树),然后您的代码才可以使用 DOM 的标准接口来操作这个树结构。但大部分情况下我们只对文档的部分内容感兴趣,根本就不用先解析整个文档,并且从节点树的根节点来索引一些我们需要的数据也是非常耗时的。 

SAX是一种XML解析的替代方法。相比于文档对象模型DOM,SAX 是读取和操作 XML 数据的更快速、更轻量的方法。SAX 允许您在读取文档时处理它,从而不必等待整个文档被存储之后才采取操作。它不涉及 DOM 所必需的开销和概念跳跃。 SAX API是一个基于事件的API ,适用于处理数据流,即随着数据的流动而依次处理数据。SAX API 在其解析您的文档时发生一定事件的时候会通知您。在您对其响应时,您不作保存的数据将会 被抛弃。

下面是一个SAX解析XML的示例(有点长,因为详细注解了SAX事件处理的所有方法),SAX API中主要有四种处理事件的接口,它们分别是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler 。下面的例子可能有点冗长,实际上只要继承DefaultHandler 类 ,再覆盖一部分 处理事件的方法 同样可以达到这个示例的效果,但为了纵观全局,还是看看SAX API里面所有主要的事件解析方法吧。( 实际上DefaultHandler就是实现了上面的四个事件处理器接口,然后提供了每个抽象方法的默认实现。) 

1,ContentHandler 接口 :接收文档逻辑内容的通知 的处理器接口。

java" id="highlighter_350639">
 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
 
class MyContentHandler implements ContentHandler{
 StringBuffer jsonStringBuffer ;
 int frontBlankCount = 0;
 public MyContentHandler(){
  jsonStringBuffer = new StringBuffer();
 }
 /*
  * 接收字符数据的通知。
  * 在DOM中 ch[begin:end] 相当于Text节点的节点值(nodeValue)
  */
 @Override
 public void characters(char[] ch, int begin, int length) throws SAXException {
  StringBuffer buffer = new StringBuffer();
  for(int i = begin ; i < begin+length ; i++){
   switch(ch[i]){
    case '\\':buffer.append("\\\\");break;
    case '\r':buffer.append("\\r");break;
    case '\n':buffer.append("\\n");break;
    case '\t':buffer.append("\\t");break;
    case '\"':buffer.append("\\\"");break;
    default : buffer.append(ch[i]);
   }
  }
  System.out.println(this.toBlankString(this.frontBlankCount)+
    ">>> characters("+length+"): "+buffertoString());
 }
 
  
 /*
  * 接收文档的结尾的通知。
  */
 @Override
 public void endDocument() throws SAXException {
  System.out.println(this.toBlankString(--this.frontBlankCount)+
    ">>> end document");
 }
 
  
 /*
  * 接收文档的结尾的通知。
  * 参数意义如下:
  * uri :元素的命名空间
  * localName :元素的本地名称(不带前缀)
  * qName :元素的限定名(带前缀)
  *
  */
 @Override
 public void endElement(String uri,String localName,String qName)
   throws SAXException {
  System.out.println(this.toBlankString(--this.frontBlankCount)+
    ">>> end element : "+qName+"("+uri+")");
 }
 
 /*
  * 结束前缀 URI 范围的映射。
  */
 @Override
 public void endPrefixMapping(String prefix) throws SAXException {
  System.out.println(this.toBlankString(--this.frontBlankCount)+
    ">>> end prefix_mapping : "+prefix);
 }
 
 /*
  * 接收元素内容中可忽略的空白的通知。
  * 参数意义如下:
  *  ch : 来自 XML 文档的字符
  *  start : 数组中的开始位置
  *  length : 从数组中读取的字符的个数
  */
 @Override
 public void ignorableWhitespace(char[] ch, int begin, int length)
   throws SAXException {
  StringBuffer buffer = new StringBuffer();
  for(int i = begin ; i < begin+length ; i++){
   switch(ch[i]){
    case '\\':bufferappend("\\\\");break;
    case '\r':bufferappend("\\r");break;
    case '\n':bufferappend("\\n");break;
    case '\t':bufferappend("\\t");break;
    case '\"':bufferappend("\\\"");break;
    default : bufferappend(ch[i]);
   }
  }
  System.out.println(this.toBlankString(this.frontBlankCount)+">>> ignorable whitespace("+length+"): "+buffer.toString());
 }
  
 /*
  * 接收处理指令的通知。
  * 参数意义如下:
  *  target : 处理指令目标
  *  data : 处理指令数据,如果未提供,则为 null。
  */
 @Override
 public void processingInstruction(String target,String data)
   throws SAXException {
  System.out.println(this.toBlankString(this.frontBlankCount)+">>> process instruction : (target = \""
    +target+"\",data = \""+data+"\")");
 }
 
 /*
  * 接收用来查找 SAX 文档事件起源的对象。
  * 参数意义如下:
  *  locator : 可以返回任何 SAX 文档事件位置的对象
  */
 @Override
 public void setDocumentLocator(Locator locator) {
  System.out.println(this.toBlankString(this.frontBlankCount)+
    ">>> set document_locator : (lineNumber = "+locatorgetLineNumber()
    +",columnNumber = "+locatorgetColumnNumber()
    +",systemId = "+locatorgetSystemId()
    +",publicId = "+locatorgetPublicId()+")");
   
 }
 
 /*
  * 接收跳过的实体的通知。
  * 参数意义如下:
  *  name : 所跳过的实体的名称。如果它是参数实体,则名称将以 '%' 开头,
  *   如果它是外部 DTD 子集,则将是字符串 "[dtd]"
  */
 @Override
 public void skippedEntity(String name) throws SAXException {
  System.out.println(this.toBlankString(this.frontBlankCount)+
    ">>> skipped_entity : "+name);
 }
 
 /*
  * 接收文档的开始的通知。
  */
 @Override
 public void startDocument() throws SAXException {
  System.out.println(this.toBlankString(this.frontBlankCount++)+
    ">>> start document ");
 }
 
 /*
  * 接收元素开始的通知。
  * 参数意义如下:
  * uri :元素的命名空间
  * localName :元素的本地名称(不带前缀)
  * qName :元素的限定名(带前缀)
  * atts :元素的属性集合
  */
 @Override
 public void startElement(String uri, String localName, String qName,
   Attributes atts) throws SAXException {
  System.out.println(this.toBlankString(this.frontBlankCount++)+
    ">>> start element : "+qName+"("+uri+")");
 }
  
 /*
  * 开始前缀 URI 名称空间范围映射。
  * 此事件的信息对于常规的命名空间处理并非必需:
  * 当 http://xmlorg/sax/features/namespaces 功能为 true(默认)时,
  * SAX XML 读取器将自动替换元素和属性名称的前缀。
  * 参数意义如下:
  * prefix :前缀
  * uri :命名空间
  */
 @Override
 public void startPrefixMapping(String prefix,String uri)
   throws SAXException {
  System.out.println(this.toBlankString(this.frontBlankCount++)+
    ">>> start prefix_mapping : xmlns:"+prefix+" = "
    +"\""+uri+"\"");
   
 }
  
 private String toBlankString(int count){
  StringBuffer buffer = new StringBuffer();
  for(int i = 0;i<count;i++)
   buffer.append(" ");
  return buffer.toString();
 }
  
}

2,DTDHandler 接口 :接收与 DTD 相关的事件的通知的处理器接口。

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import org.xml.sax.DTDHandler;
import org.xml.sax.SAXException;
 
public class MyDTDHandler implements DTDHandler {
 
 /*
  * 接收注释声明事件的通知。
  * 参数意义如下:
  *  name - 注释名称。
  *  publicId - 注释的公共标识符,如果未提供,则为 null。
  *  systemId - 注释的系统标识符,如果未提供,则为 null。
  */
 @Override
 public void notationDecl(String name, String publicId, String systemId)
   throws SAXException {
  Systemoutprintln(">>> notation declare : (name = "+name
    +",systemId = "+publicId
    +",publicId = "+systemId+")");
 }
 
 /*
  * 接收未解析的实体声明事件的通知。
  * 参数意义如下:
  *  name - 未解析的实体的名称。
  *  publicId - 实体的公共标识符,如果未提供,则为 null。
  *  systemId - 实体的系统标识符。
  *  notationName - 相关注释的名称。
  */
 @Override
 public void unparsedEntityDecl(String name,
   String publicId,
   String systemId,
   String notationName) throws SAXException {
  Systemoutprintln(">>> unparsed entity declare : (name = "+name
    +",systemId = "+publicId
    +",publicId = "+systemId
    +",notationName = "+notationName+")");
 }
 
}

3,EntityResolver 接口 :是用于解析实体的基本接口。

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.IOException;
 
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
 
public class MyEntityResolver implements EntityResolver {
 
 /*
  * 允许应用程序解析外部实体。
  * 解析器将在打开任何外部实体(*文档实体除外)前调用此方法
  * 参数意义如下:
  *  publicId : 被引用的外部实体的公共标识符,如果未提供,则为 null。
  *  systemId : 被引用的外部实体的系统标识符。
  * 返回:
  *  一个描述新输入源的 InputSource 对象,或者返回 null,
  *  以请求解析器打开到系统标识符的常规 URI 连接。
  */
 @Override
 public InputSource resolveEntity(String publicId, String systemId)
   throws SAXException, IOException {
  return null;
 }
 
}

4,ErrorHandler接口 :是错误处理程序的基本接口。

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
 
public class MyErrorHandler implements ErrorHandler {
 
 /*
  * 接收可恢复的错误的通知
  */
 @Override
 public void error(SAXParseException e) throws SAXException {
  System.err.println("Error ("+e.getLineNumber()+","
    +e.getColumnNumber()+") : "+e.getMessage());
 }
  
 /*
  * 接收不可恢复的错误的通知。
  */
 @Override
 public void fatalError(SAXParseException e) throws SAXException {
  System.err.println("FatalError ("+e.getLineNumber()+","
    +e.getColumnNumber()+") : "+e.getMessage());
 }
 
 /*
  * 接收不可恢复的错误的通知。
  */
 @Override
 public void warning(SAXParseException e) throws SAXException {
  System.err.println("Warning ("+e.getLineNumber()+","
    +e.getColumnNumber()+") : "+e.getMessage());
 }
 
}

Test 类的主方法打印解析books.xml时的事件信息。

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
 
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
 
 
public class Test {
 
 public static void main(String[] args) throws SAXException,
   FileNotFoundException, IOException {
  //创建处理文档内容相关事件的处理器
  ContentHandler contentHandler = new MyContentHandler();
  //创建处理错误事件处理器
  ErrorHandler errorHandler = new MyErrorHandler();
  //创建处理DTD相关事件的处理器
  DTDHandler dtdHandler = new MyDTDHandler();
  //创建实体解析器
  EntityResolver entityResolver = new MyEntityResolver();
   
  //创建一个XML解析器(通过SAX方式读取解析XML)
  XMLReader reader = XMLReaderFactory.createXMLReader();
  /*
   * 设置解析器的相关特性
   http://xml.org/sax/features/validation = true 表示开启验证特性
   http://xml.org/sax/features/namespaces = true 表示开启命名空间特性
   */
  reader.setFeature("http://xml.org/sax/features/validation",true);
  reader.setFeature("http://xml.org/sax/features/namespaces",true);
  //设置XML解析器的处理文档内容相关事件的处理器
  reader.setContentHandler(contentHandler);
  //设置XML解析器的处理错误事件处理器
  reader.setErrorHandler(errorHandler);
  //设置XML解析器的处理DTD相关事件的处理器
  reader.setDTDHandler(dtdHandler);
  //设置XML解析器的实体解析器
  reader.setEntityResolver(entityResolver);
  //解析books.xml文档
  reader.parse(new InputSource(new FileReader("books.xml")));
 }
 
}

books.xml 文件的内容如下:

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="GB2312"?>
<books count="3" xmlns="http://testorg/books">
 <!--books's comment-->
 <book id="1">
  <name>Thinking in JAVA</name>
 </book>
 <book id="2">
  <name>Core JAVA2</name>
 </book>
 <book id="3">
  <name>C++ primer</name>
 </book>
</books>

控制台输出如下:

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
>>> set document_locator : (lineNumber = 1,columnNumber = 1,systemId = null,publicId = null)
>>> start document
Error (2,7) : Document is invalid: no grammar found.
Error (2,7) : Document root element "books", must match DOCTYPE root "null".
 >>> start prefix_mapping : xmlns: = "http://test.org/books"
  >>> start element : books(http://test.org/books)
   >>> characters(2): \n\t
   >>> characters(2): \n\t
   >>> start element : book(http://test.org/books)
    >>> characters(3): \n\t\t
    >>> start element : name(http://test.org/books)
     >>> characters(16): Thinking in JAVA
    >>> end element : name(http://test.org/books)
    >>> characters(2): \n\t
   >>> end element : book(http://test.org/books)
   >>> characters(2): \n\t
   >>> start element : book(http://test.org/books)
    >>> characters(3): \n\t\t
    >>> start element : name(http://test.org/books)
     >>> characters(10): Core JAVA2
    >>> end element : name(http://test.org/books)
    >>> characters(2): \n\t
   >>> end element : book(http://test.org/books)
   >>> characters(2): \n\t
   >>> start element : book(http://test.org/books)
    >>> characters(3): \n\t\t
    >>> start element : name(http://test.org/books)
     >>> characters(10): C++ primer
    >>> end element : name(http://test.org/books)
    >>> characters(2): \n\t
   >>> end element : book(http://test.org/books)
   >>> characters(1): \n
  >>> end element : books(http://test.org/books)
 >>> end prefix_mapping :
>>> end document