Cursor 和 Iterator API 的区别
在读取 XML 文档时,迭代器读取器从其nextEvent()调用中返回一个 XML 事件对象。此事件提供有关我们遇到的 XML 标记类型(元素、文本、注释等)的信息。收到的事件是不可变的,因此我们可以传递应用程序以安全地处理它。
XMLEventReader reader = ...;
while(reader.hasNext()){
XMLEvent event = reader.nextEvent();
if(event.getEventType() == XMLEvent.START_ELEMENT){
//process data
}
//... more event types handled here...
}
与 Iterator 不同,游标的工作方式类似于 JDBC 中的 Resultset。如果光标移动到 XML 文档中的下一个元素。然后,您可以直接在游标上调用方法来获取有关当前事件的更多信息。
XMLStreamReader streamReader = ...;
while(streamReader.hasNext()){
int eventType = streamReader.next();
if(eventType == XMLStreamReader.START_ELEMENT){
System.out.println(streamReader.getLocalName());
}
//... more event types handled here...
}
迭代器 API 示例
下面演示了如何使用 StAX 基于迭代器的API 将 XML 文档读取到对象。
XML文件如下:
<employees>
<employee id="101">
<name>Lokesh Gupta</name>
<title>Author</title>
</employee>
<employee id="102">
<name>Brian Lara</name>
<title>Cricketer</title>
</employee>
</employees>
为了读取该文件,我按以下步骤编写了程序:
-
创建一个迭代器并开始接收事件。
-
一旦你得到
open 'employee' tag
– 创建新Employee
对象。 -
从员工标签中读取
id
属性并将其设置为当前对象。Employee
-
迭代到下一个开始标记事件。这些是
employee
标记内的 XML 元素。读取这些标签内的数据。将读取的数据设置为当前Employee
对象。 -
继续迭代该事件。当你找到 tag 的结束元素事件时
'employee'
,你可以说你已经读取了 current 的数据employee
,因此将当前employee
对象添加到employeeList
集合中。 -
最后通过打印验证读取的数据
employeeList
。
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 ReadXmlWithIterator {
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
}
}
程序输出:
Cursor API示例
我将读取同一个employees.xml
文件——现在使用基于游标的 API。
package xml.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 ReadXmlWithCursor {
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);
}
}
总结
这两个 API 都能够解析任何类型的 XML 文档,但Cursor API 比迭代器 API 更节省内存。因此,如果您的应用程序需要更好的性能,请考虑使用基于游标的 API。