Java SAX
最后修改于 2024 年 1 月 27 日
Java SAX 教程展示了如何使用 Java SAX API 读取和验证 XML 文档。
SAX
SAX (XML 简单 API)是一种事件驱动的 XML 文档解析算法。 SAX 是文档对象模型 (DOM) 的替代方案。 DOM 读取整个文档以操作 XML,而 SAX 解析器逐个节点地读取 XML,并在遍历输入流时发出解析事件。 SAX 以状态无关的方式处理文档(元素的处理不依赖于之前的元素)。 SAX 解析器是只读的。
SAX 解析器速度更快,占用内存更少。 另一方面,DOM 更易于使用,并且有些任务,例如对元素进行排序、重新排列元素或查找元素,使用 DOM 会更快。
SAX 解析器随 JDK 一起提供,因此无需下载依赖项。
Java SAX 解析示例
在以下示例中,我们使用 SAX 解析器读取 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 文件。
package com.zetcode.model; 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(); } }
这是 user bean;它将保存来自 XML 节点的数据。
package com.zetcode; import com.zetcode.model.User; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; 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; public class MyRunner { private SAXParser saxParser = null; private SAXParser createSaxParser() { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("https://apache.ac.cn/xml/features/disallow-doctype-decl", true); 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() { var 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(); factory.setFeature("https://apache.ac.cn/xml/features/disallow-doctype-decl", true); saxParser = factory.newSAXParser();
我们从 SAXParserFactory
获取 SAXParser
。
SAXParser parser = createSaxParser(); parser.parse(xmlDocument, handler);
我们使用 parse
方法解析文档。 该方法的第二个参数是处理程序对象,其中包含事件处理程序。
package com.zetcode; import com.zetcode.model.User; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import java.util.ArrayList; import java.util.List; 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) { if ("user".equals(qName)) { user = new User(); int id = Integer.parseInt(attributes.getValue("id")); user.setId(id); } switch (qName) { case "firstname" -> bfn = true; case "lastname" -> bln = true; case "occupation" -> boc = true; } } @Override public void characters(char[] ch, int start, int length) { 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) { 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) { if ("user".equals(qName)) { user = new User(); int id = Integer.parseInt(attributes.getValue("id")); user.setId(id); } switch (qName) { case "firstname" -> bfn = true; case "lastname" -> bln = true; case "occupation" -> boc = true; } }
当解析器开始解析新元素时,调用 startElement
方法。 如果元素是 <user>
,我们创建一个新用户。 对于其他类型的元素,我们设置布尔值。
@Override public void characters(char[] ch, int start, int length) { 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) { if ("user".equals(qName)) { users.add(user); } }
在 <user>
元素结束时,我们将用户对象添加到用户列表中。
package com.zetcode; import com.zetcode.model.User; import java.util.List; public class JavaReadXmlSaxEx { public static void main(String[] args) { var runner = new MyRunner(); List<User> lines = runner.parseUsers(); lines.forEach(System.out::println); } }
JavaReadXmlSaxEx
启动应用程序。 它将解析任务委托给 MyRunner
。 最后,检索到的数据会打印到控制台。
Java SAX 验证示例
以下示例使用 XSD 语言验证 XML 文件。 XSD(XML 模式定义)是所有 XML 文档和数据的当前标准模式语言。 (还有其他替代的模式语言,例如 DTD 和 RELAX NG。)XSD 是一组规则,XML 文档必须符合这些规则才能被视为根据该模式有效。
<?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
属性必须是整数并且是强制性的。
<?xml version="1.0" encoding="UTF-8"?> <users> ...
我们有相同的文件。
package com.zetcode; import org.xml.sax.InputSource; import org.xml.sax.SAXException; 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 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; public class JavaSaxValidation { public static void main(String[] args) { var 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); factory.setFeature("https://apache.ac.cn/xml/features/disallow-doctype-decl", true); Schema schema = factory.newSchema(xsdFile); Validator validator = schema.newValidator(); var source = new SAXSource(new InputSource(reader)); validator.validate(source); System.out.println("The document was validated OK"); } catch (SAXException ex) { Logger lgr = Logger.getLogger(JavaSaxValidation.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(JavaSaxValidation.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); factory.setFeature("https://apache.ac.cn/xml/features/disallow-doctype-decl", true); Schema schema = factory.newSchema(xsdFile);
使用 SchemaFactory
,我们为模式定义选择 W3C XML 模式。 换句话说,我们的自定义模式定义也必须遵守某些规则。
Validator validator = schema.newValidator();
从模式生成一个新的验证器。
var 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
。
来源
在本文中,我们使用 Java SAX 读取并验证了 XML 文档。
作者
列出所有Java教程。