53.md 10.0 KB
Newer Older
W
wizardforcel 已提交
1
# 使用 StAX Parser 的 Java 读取 XML – 游标和迭代器 API
W
wizardforcel 已提交
2 3 4

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

W
wizardforcel 已提交
5
学习使用 Java StAX 解析器解析和**读取 XML 文件**。 StAX(用于 XML 的流 API)提供了两种解析 XML 的方法,即**基于游标的 API****基于迭代器的 API**
W
wizardforcel 已提交
6 7 8 9 10

## 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 流。 区别在于:

W
wizardforcel 已提交
11
1.  StAX 是“`pull`” API。 SAX 是“`push`” API。
W
wizardforcel 已提交
12 13
2.  StAX 可以进行 XML 读取和写入。 SAX 只能读取 XML。

W
wizardforcel 已提交
14
**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 信息。
W
wizardforcel 已提交
15 16 17

#### 游标与迭代器

W
wizardforcel 已提交
18
1.  在读取 XML 文档时,迭代器读取器从其`nextEvent()`调用中返回 XML 事件对象。 此事件提供有关您遇到的 XML 标签类型(元素,文本,注释等)的信息。 收到的事件是不可变的,因此您可以传递应用以安全地对其进行处理。
W
wizardforcel 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

    ```java
    XMLEventReader reader = ...;

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

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

    ```

W
wizardforcel 已提交
34
2.  与迭代器不同,游标的工作方式类似于 JDBC 中的`Resultset`。 如果将游标移动到 XML 文档中的下一个元素。 然后,您可以直接在游标上调用方法以获得有关当前事件的更多信息。
W
wizardforcel 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

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

    ```

W
wizardforcel 已提交
51
## 2)StAX 迭代器 API 示例
W
wizardforcel 已提交
52

W
wizardforcel 已提交
53
下面给出的 XML 文档演示**如何使用基于 StAX 迭代器的 API 读取对象**
W
wizardforcel 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

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

```

W
wizardforcel 已提交
71
#### 使用 StAX 迭代器读取 XML
W
wizardforcel 已提交
72 73 74 75

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

1.  创建迭代器并开始接收事件。
W
wizardforcel 已提交
76
2.  一旦获得打开的`'employee'`标签,请创建一个新的`Employee`对象。
W
wizardforcel 已提交
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 182 183 184 185 186 187
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]]

```

W
wizardforcel 已提交
188
## 3)StAX 游标 API 示例
W
wizardforcel 已提交
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

我将使用基于游标的 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。**
W
wizardforcel 已提交
279
4.  如何通过示例使用 StAX 游标 API 来**读取 XML。**
W
wizardforcel 已提交
280

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

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

学习愉快!