Java SAX 教程展示了如何使用 Java SAX API 来读取和验证 XML 文档。
SAX
SAX(XML 的简单 API)是事件驱动的算法,用于解析 XML 文档。 SAX 是文档对象模型(DOM)的替代方法。 在 DOM 读取整个文档以对 XML 进行操作的地方,SAX 解析器逐个节点读取 XML,发出解析事件,同时逐步遍历输入流。 SAX 独立于状态处理文档(元素的处理不依赖于之前的元素)。 SAX 解析器是只读的。
SAX 解析器更快并且需要更少的内存。 另一方面,DOM 更易于使用,并且有些任务(例如,排序元素,重新排列元素或查找元素)使用 DOM 更快。
SADK 解析器是 JDK 附带的,因此不需要下载依赖项。
Java SAX 解析示例
在下面的示例中,我们使用 SAX 解析器读取 XML 文件。
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>com.zetcode.JavaReadXmlSaxEx</mainClass>
</configuration>
</plugin>
</plugins>
</build>
我们使用exec-maven-plugin
从 Maven 执行 Java 主类。
users.xml
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<firstname>Peter</firstname>
<lastname>Brown</lastname>
<occupation>programmer</occupation>
</user>
<user id="2">
<firstname>Martin</firstname>
<lastname>Smith</lastname>
<occupation>accountant</occupation>
</user>
<user id="3">
<firstname>Lucy</firstname>
<lastname>Gordon</lastname>
<occupation>teacher</occupation>
</user>
</users>
我们将阅读此 XML 文件。
User.java
package com.zetcode;
public class User {
int id;
private String firstName;
private String lastName;
private String occupation;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("User{").append("id=").append(id)
.append(", firstName=").append(firstName)
.append(", lastName=").append(lastName)
.append(", occupation=").append(occupation).append("}");
return builder.toString();
}
}
这是用户 bean。 它将保存来自 XML 节点的数据。
MyRunner.java
package com.zetcode;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.SAXException;
public class MyRunner {
private SAXParser createSaxParser() {
SAXParser saxParser = null;
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
saxParser = factory.newSAXParser();
return saxParser;
} catch (ParserConfigurationException | SAXException ex) {
Logger lgr = Logger.getLogger(MyRunner.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
return saxParser;
}
public List<User> parseUsers() {
MyHandler handler = new MyHandler();
String fileName = "src/main/resources/users.xml";
File xmlDocument = Paths.get(fileName).toFile();
try {
SAXParser parser = createSaxParser();
parser.parse(xmlDocument, handler);
} catch (SAXException | IOException ex) {
Logger lgr = Logger.getLogger(MyRunner.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
return handler.getUsers();
}
}
MyRunner
创建一个 SAX 解析器并启动解析。 parseUsers
返回User
对象列表中的解析数据。
SAXParserFactory factory = SAXParserFactory.newInstance();
saxParser = factory.newSAXParser();
从SAXParserFactory
获得SAXParser
。
SAXParser parser = createSaxParser();
parser.parse(xmlDocument, handler);
我们使用parse()
方法解析文档。 方法的第二个参数是处理程序对象,其中包含事件处理程序。
MyHandler.java
package com.zetcode;
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class MyHandler extends DefaultHandler {
private List<User> users = new ArrayList<>();
private User user;
private boolean bfn = false;
private boolean bln = false;
private boolean boc = false;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if ("user".equals(qName)) {
user = new User();
int id = Integer.valueOf(attributes.getValue("id"));
user.setId(id);
}
switch (qName) {
case "firstname":
bfn = true;
break;
case "lastname":
bln = true;
break;
case "occupation":
boc = true;
break;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (bfn) {
user.setFirstName(new String(ch, start, length));
bfn = false;
}
if (bln) {
user.setLastName(new String(ch, start, length));
bln = false;
}
if (boc) {
user.setOccupation(new String(ch, start, length));
boc = false;
}
}
@Override
public void endElement(String uri, String localName,
String qName) throws SAXException {
if ("user".equals(qName)) {
users.add(user);
}
}
public List<User> getUsers() {
return users;
}
}
在MyHandler
类中,我们具有事件处理程序的实现。
public class MyHandler extends DefaultHandler {
处理程序类必须从具有事件方法的DefaultHandler
扩展。
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if ("user".equals(qName)) {
user = new User();
int id = Integer.valueOf(attributes.getValue("id"));
user.setId(id);
}
switch (qName) {
case "firstname":
bfn = true;
break;
case "lastname":
bln = true;
break;
case "occupation":
boc = true;
break;
}
}
当解析器开始解析新元素时,将调用startElement()
方法。 如果元素为<user>
,我们将创建一个新用户。 对于其他类型的元素,我们设置布尔值。
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (bfn) {
user.setFirstName(new String(ch, start, length));
bfn = false;
}
if (bln) {
user.setLastName(new String(ch, start, length));
bln = false;
}
if (boc) {
user.setOccupation(new String(ch, start, length));
boc = false;
}
}
当解析器在元素内部遇到文本时,将调用characters()
方法。 根据布尔变量,我们设置用户属性。
@Override
public void endElement(String uri, String localName,
String qName) throws SAXException {
if ("user".equals(qName)) {
users.add(user);
}
}
在<user>
元素的末尾,我们将用户对象添加到用户列表中。
JavaReadXmlSaxEx.java
package com.zetcode;
import java.util.List;
public class JavaReadXmlSaxEx {
public static void main(String[] args) {
MyRunner runner = new MyRunner();
List<User> lines = runner.parseUsers();
lines.forEach(System.out::println);
}
}
JavaReadXmlSaxEx
启动应用。 它将解析任务委托给MyRunner
。 最后,检索到的数据将打印到控制台。
$ mvn exec:java -q
User{id=1, firstName=Peter, lastName=Brown, occupation=programmer}
User{id=2, firstName=Martin, lastName=Smith, occupation=accountant}
User{id=3, firstName=Lucy, lastName=Gordon, occupation=teacher}
这是示例的输出。
Java SAX 验证示例
以下示例使用 XSD 语言来验证 XML 文件。 XSD(XML 架构定义)是所有 XML 文档和数据的当前标准架构语言。 (还有其他替代的模式语言,例如 DTD 和 RELAX NG。)XSD 是 XML 文档必须遵循的一组规则,以便根据该模式被视为有效。
users.xsd
<?xml version="1.0"?>
<xs:schema version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="firstname"/>
<xs:element type="xs:string" name="lastname"/>
<xs:element type="xs:string" name="occupation"/>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
这是用于验证用户的 XSD 文件。 例如,它声明<user>
元素必须在<users>
元素之内,或者<user>
的id
属性必须是且是整数,并且是强制性的。
JavaXmlSchemaValidationEx.java
package com.zetcode;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class JavaXmlSchemaValidationEx {
public static void main(String[] args) {
File xsdFile = new File("src/main/resources/users.xsd");
try {
Path xmlPath = Paths.get("src/main/resources/users.xml");
Reader reader = Files.newBufferedReader(xmlPath);
String schemaLang = XMLConstants.W3C_XML_SCHEMA_NS_URI;
SchemaFactory factory = SchemaFactory.newInstance(schemaLang);
Schema schema = factory.newSchema(xsdFile);
Validator validator = schema.newValidator();
SAXSource source = new SAXSource(new InputSource(reader));
validator.validate(source);
System.out.println("The document was validated OK");
} catch (SAXException ex) {
Logger lgr = Logger.getLogger(JavaXmlSchemaValidationEx.class.getName());
lgr.log(Level.SEVERE, "The document failed to validate");
lgr.log(Level.SEVERE, ex.getMessage(), ex);
} catch (IOException ex) {
Logger lgr = Logger.getLogger(JavaXmlSchemaValidationEx.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
}
}
该示例使用users.xsd
模式来验证users.xml
文件。
String schemaLang = XMLConstants.W3C_XML_SCHEMA_NS_URI;
SchemaFactory factory = SchemaFactory.newInstance(schemaLang);
Schema schema = factory.newSchema(xsdFile);
使用SchemaFactory
,我们为我们的模式定义选择 W3C XML 模式。 换句话说,我们的自定义架构定义还必须遵守某些规则。
Validator validator = schema.newValidator();
从架构生成一个新的验证器。
SAXSource source = new SAXSource(new InputSource(reader));
validator.validate(source);
我们根据提供的模式验证 XML 文档。
} catch (SAXException ex) {
Logger lgr = Logger.getLogger(JavaXmlSchemaValidationEx.class.getName());
lgr.log(Level.SEVERE, "The document failed to validate");
lgr.log(Level.SEVERE, ex.getMessage(), ex);
}
默认情况下,如果文档无效,则抛出SAXException
。