# 使用 StAX Parser 的 Java 读取 XML – 游标和迭代器 API

> 原文： [https://howtodoinjava.com/xml/read-xml-stax-parser-cursor-iterator/](https://howtodoinjava.com/xml/read-xml-stax-parser-cursor-iterator/)

学习使用 Java StAX 解析器解析和**读取 XML 文件**。 StAX（用于 XML 的流 API）提供了两种解析 XML 的方法，即**基于游标的 API** 和**基于迭代器的 API** 。

## 1）StAX 解析器

就像 [SAX 解析器](https://howtodoinjava.com/xml/how-to-parse-an-xml-using-sax-parser-and-defaulthandler/)一样， [StAX API](https://docs.oracle.com/javase/tutorial/jaxp/stax/api.html) 设计用于解析 XML 流。 区别在于：

1.  StAX 是“`pull`” API。 SAX 是“`push`” API。
2.  StAX 可以进行 XML 读取和写入。 SAX 只能读取 XML。

**StAX 是拉取样式 API** 。 这意味着您必须自己将 StAX 解析器从 XML 文件中的一个项目移动到另一个项目，就像使用标准[`Iterator`](https://howtodoinjava.com/java/collections/how-iterator-works-in-java/)或 JDBC [`ResultSet`](https://howtodoinjava.com/java/jdbc/jdbc-select-query-example/)一样。 然后，您可以通过 StAX 解析器访问 XML 文件中遇到的每个此类“项目”的 XML 信息。

#### 游标与迭代器

1.  在读取 XML 文档时，迭代器读取器从其`nextEvent()`调用中返回 XML 事件对象。 此事件提供有关您遇到的 XML 标签类型（元素，文本，注释等）的信息。 收到的事件是不可变的，因此您可以传递应用以安全地对其进行处理。

    ```java
    XMLEventReader reader = ...;

    while(reader.hasNext()){
        XMLEvent event = reader.nextEvent();

        if(event.getEventType() == XMLEvent.START_ELEMENT){
            //process data
        }	
        //... more event types handled here...
    }

    ```

2.  与迭代器不同，游标的工作方式类似于 JDBC 中的`Resultset`。 如果将游标移动到 XML 文档中的下一个元素。 然后，您可以直接在游标上调用方法以获得有关当前事件的更多信息。

    ```java
    XMLStreamReader streamReader = ...;

    while(streamReader.hasNext()){
        int eventType = streamReader.next();

        if(eventType == XMLStreamReader.START_ELEMENT){
            System.out.println(streamReader.getLocalName());
        }

        //... more event types handled here...
    }

    ```

## 2）StAX 迭代器 API 示例

下面给出的 XML 文档演示**如何使用基于 StAX 迭代器的 API 读取对象**。

#### XML 文件

```java
<employees>
	<employee id="101">
		 <name>Lokesh Gupta</name>
	    <title>Author</title>
	</employee>
	<employee id="102">
		 <name>Brian Lara</name>
	    <title>Cricketer</title>
	</employee>
</employees>

```

#### 使用 StAX 迭代器读取 XML

要读取文件，我已按照以下步骤编写了程序：

1.  创建迭代器并开始接收事件。
2.  一旦获得打开的`'employee'`标签，请创建一个新的`Employee`对象。
3.  从员工标签读取`id`属性，并将其设置为当前的`Employee`对象。
4.  循环到下一个开始标签事件。 这些是`employee`标记内的 XML 元素。 读取这些标签内的数据。 将读取的数据设置为当前的`Employee`对象。
5.  继续迭代事件。 当找到`'employee'`标签的结束元素事件时，可以说您已经读取了当前`employee`的数据，因此将当前`employee`对象添加到`employeeList`集合中。
6.  最后，通过打印`employeeList`验证读取的数据。

```java
package com.howtodoinjava.demo.stax;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

public class ReadXMLExample 
{
	public static void main(String[] args) throws FileNotFoundException, XMLStreamException 
	{
		 File file = new File("employees.xml");

		// Instance of the class which helps on reading tags
	    XMLInputFactory factory = XMLInputFactory.newInstance();

        // Initializing the handler to access the tags in the XML file
        XMLEventReader eventReader = factory.createXMLEventReader(new FileReader(file));

        //All read employees objects will be added to this list
        List<Employee> employeeList = new ArrayList<>();

        //Create Employee object. It will get all the data using setter methods.
        //And at last, it will stored in above 'employeeList' 
        Employee employee = null;

        // Checking the availability of the next tag
        while (eventReader.hasNext())
        {
        	XMLEvent xmlEvent = eventReader.nextEvent();

        	if (xmlEvent.isStartElement())
        	{
        		StartElement startElement = xmlEvent.asStartElement();

        		//As soo as employee tag is opened, create new Employee object
        		if("employee".equalsIgnoreCase(startElement.getName().getLocalPart())) {
        			employee = new Employee();	
        		}

        		//Read all attributes when start tag is being read
        		@SuppressWarnings("unchecked")
				Iterator<Attribute> iterator = startElement.getAttributes();

                while (iterator.hasNext())
                {
                    Attribute attribute = iterator.next();
                    QName name = attribute.getName();
                    if("id".equalsIgnoreCase(name.getLocalPart())) {
                    	employee.setId(Integer.valueOf(attribute.getValue()));
                    }
                }

                //Now everytime content tags are found; 
                //Move the iterator and read data
        		switch (startElement.getName().getLocalPart()) 
        		{
	        		case "name":
	        			Characters nameDataEvent = (Characters) eventReader.nextEvent();
	        			employee.setName(nameDataEvent.getData());
	        			break;

	        		case "title":
	        			Characters titleDataEvent = (Characters) eventReader.nextEvent();
	        			employee.setTitle(titleDataEvent.getData());
	        			break;
        		}
        	}

        	if (xmlEvent.isEndElement())
        	{
        		EndElement endElement = xmlEvent.asEndElement();

        		//If employee tag is closed then add the employee object to list; 
        		//and be ready to read next employee data
        		if("employee".equalsIgnoreCase(endElement.getName().getLocalPart())) {
        			employeeList.add(employee);
        		}
        	}
        }

        System.out.println(employeeList);	//Verify read data

	}
}

//Output:

[Employee [id=101, name=Lokesh Gupta, title=Author], 
 Employee [id=102, name=Brian Lara,   title=Cricketer]]

```

## 3）StAX 游标 API 示例

我将使用基于游标的 API 读取相同的`employees.xml`文件。

```java
package com.howtodoinjava.demo.stax;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

public class ReadXMLExample 
{
	public static void main(String[] args) throws FileNotFoundException, XMLStreamException 
	{
		//All read employees objects will be added to this list
        List<Employee> employeeList = new ArrayList<>();

        //Create Employee object. It will get all the data using setter methods.
        //And at last, it will stored in above 'employeeList' 
        Employee employee = null;

        File file = new File("employees.xml");
	    XMLInputFactory factory = XMLInputFactory.newInstance();
	    XMLStreamReader streamReader = factory.createXMLStreamReader(new FileReader(file));

	    while(streamReader.hasNext())
	    {
	    	//Move to next event
	        streamReader.next();

	        //Check if its 'START_ELEMENT'
	        if(streamReader.getEventType() == XMLStreamReader.START_ELEMENT)
	        {
	        	//employee tag - opened
	            if(streamReader.getLocalName().equalsIgnoreCase("employee")) {

	            	//Create new employee object asap tag is open
	            	employee = new Employee();	

	            	//Read attributes within employee tag
	            	if(streamReader.getAttributeCount() > 0) {
	            		String id = streamReader.getAttributeValue(null,"id");
	            		employee.setId(Integer.valueOf(id));
	            	}
	            }

	            //Read name data
	            if(streamReader.getLocalName().equalsIgnoreCase("name")) {
	            	employee.setName(streamReader.getElementText());
	            }

	          //Read title data
	            if(streamReader.getLocalName().equalsIgnoreCase("title")) {
	            	employee.setTitle(streamReader.getElementText());
	            }
	        }

	        //If employee tag is closed then add the employee object to list
	        if(streamReader.getEventType() == XMLStreamReader.END_ELEMENT)
	        {
	        	if(streamReader.getLocalName().equalsIgnoreCase("employee")) {
	        		employeeList.add(employee);
	        	}
	        }
	    }
        //Verify read data
        System.out.println(employeeList);
	}
}

//Output:

[Employee [id=101, name=Lokesh Gupta, title=Author], 
 Employee [id=102, name=Brian Lara,   title=Cricketer]]

```

## 4）总结

因此，在此 **StAX 解析器教程**中，我们学习了以下内容：

1.  **什么是基于 XML 流 API 的 StAX 解析器**。
2.  **StAX 与 SAX** 解析器之间的差异。
3.  如何通过示例使用 StAX 迭代器 API 来**读取 XML。**
4.  如何通过示例使用 StAX 游标 API 来**读取 XML。**

两种 API 都能够解析任何类型的 XML 文档，但是**游标 API 比迭代器 API** 具有更高的内存效率。 因此，如果您的应用需要更好的性能，请考虑使用基于游标的 API。

将我的问题放在评论部分。

学习愉快！