# Java SAX 教程

原文：http://zetcode.com/java/sax/

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 文件。

```java
<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`

```java
<?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`

```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`

```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`对象列表中的解析数据。

```java
SAXParserFactory factory = SAXParserFactory.newInstance();
saxParser = factory.newSAXParser();

```

从`SAXParserFactory`获得`SAXParser`。

```java
SAXParser parser = createSaxParser();
parser.parse(xmlDocument, handler);

```

我们使用`parse()`方法解析文档。 方法的第二个参数是处理器对象，其中包含事件处理器。

`MyHandler.java`

```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`类中，我们具有事件处理器的实现。

```java
public class MyHandler extends DefaultHandler {

```

处理器类必须从具有事件方法的`DefaultHandler`扩展。

```java
@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>`，我们将创建一个新用户。 对于其他类型的元素，我们设置布尔值。

```java
@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()`方法。 根据布尔变量，我们设置用户属性。

```java
@Override
public void endElement(String uri, String localName,
        String qName) throws SAXException {

    if ("user".equals(qName)) {
        users.add(user);
    }
}

```

在`<user>`元素的末尾，我们将用户对象添加到用户列表中。

`JavaReadXmlSaxEx.java`

```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`。 最后，检索到的数据将打印到控制台。

```java
$ 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`

```java
<?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`

```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`文件。

```java
String schemaLang = XMLConstants.W3C_XML_SCHEMA_NS_URI;
SchemaFactory factory = SchemaFactory.newInstance(schemaLang);
Schema schema = factory.newSchema(xsdFile);

```

使用`SchemaFactory`，我们为我们的模式定义选择 W3C XML 模式。 换句话说，我们的自定义架构定义还必须遵守某些规则。

```java
Validator validator = schema.newValidator();

```

从架构生成一个新的验证器。

```java
SAXSource source = new SAXSource(new InputSource(reader));
validator.validate(source);

```

我们根据提供的模式验证 XML 文档。

```java
} 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 DOM 教程](/java/dom/)， [Java JAXB 教程](/java/jaxb/)， [Java JSON 处理教程](/java/jsonp/)，[提供 XML 的 Java Servlet](/articles/javaservletservexml/) 和 [Java 教程](/lang/java/)。