文档

Java™教程
隐藏目录
示例代码
路径: Java XML处理API(JAXP)
课程: XML的流式API

示例代码

本节介绍了JAXP参考实现包中包含的示例StAX代码。本节中使用的所有示例目录都位于INSTALL_DIR/jaxp-version/samples/stax目录中。

本节涵盖的主题如下:

示例代码组织

INSTALL_DIR/jaxp-version/samples/stax目录中包含六个StAX示例目录:

除了写入器示例外,所有的StAX示例都使用一个示例XML文档BookCatalog.xml

示例XML文档

大多数StAX示例类使用的示例XML文档BookCatalog.xml是一个基于通用BookCatalogue命名空间的简单图书目录。 BookCatalog.xml的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<BookCatalogue xmlns="http://www.publishing.org">
<Book>
    <Title>Yogasana Vijnana: the Science of Yoga</Title>
    <author>Dhirendra Brahmachari</Author>
    <Date>1966</Date>
    <ISBN>81-40-34319-4</ISBN>
    <Publisher>Dhirendra Yoga Publications</Publisher>
    <Cost currency="INR">11.50</Cost>
</Book>
<Book>
    <Title>The First and Last Freedom</Title>
    <Author>J. Krishnamurti</Author>
    <Date>1954</Date>
    <ISBN>0-06-064831-7</ISBN>
    <Publisher>Harper &amp; Row</Publisher>
    <Cost currency="USD">2.95</Cost>
</Book>
</BookCatalogue>

游标示例

INSTALL_DIR/jaxp-version/samples/stax/cursor/目录中,CursorParse.java演示了使用StAX游标API读取XML文档的方法。在游标示例中,应用程序通过调用next()方法来指示解析器读取XML输入流中的下一个事件。

请注意,next()只返回一个整数常数,该常数对应于解析器所在位置的底层事件。应用程序需要调用相关函数来获取与底层事件相关的更多信息。

你可以将这种方法想象成一个虚拟的游标在XML输入流中移动。当该虚拟游标位于特定事件时,可以调用各种访问器方法。

逐个步进事件

在此示例中,客户端应用程序通过调用解析器上的next方法来提取XML流中的下一个事件;例如:

try {
    for (int i = 0 ; i < count ; i++) {
        // 传递文件名...所有相对实体引用将相对于此进行解析
        // 作为基本URI。
        XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
                                   new FileInputStream(filename));

        // 当创建XMLStreamReader时,
        // 它位于START_DOCUMENT事件处。
        int eventType = xmlr.getEventType();
        printEventType(eventType);
        printStartDocument(xmlr);

        // 检查输入流中是否还有更多事件
        while(xmlr.hasNext()) {
            eventType = xmlr.next();
            printEventType(eventType);

            // 这些函数通过调用相关函数来打印有关特定事件的信息
            printStartElement(xmlr);
            printEndElement(xmlr);
            printText(xmlr);
            printPIData(xmlr);
            printComment(xmlr);
        }
    }
}

请注意,next只返回一个整数常数,该常数对应于当前游标位置下的事件。应用程序调用相关函数以获取与底层事件相关的更多信息。当游标位于特定事件时,可以调用各种访问器方法。

返回字符串表示

由于next方法只返回与底层事件类型对应的整数,通常需要将这些整数映射为事件的字符串表示形式;例如:

public final static String getEventTypeString(int eventType) {
    switch (eventType) {
        case XMLEvent.START_ELEMENT:
            return "START_ELEMENT";

        case XMLEvent.END_ELEMENT:
            return "END_ELEMENT";

        case XMLEvent.PROCESSING_INSTRUCTION:
            return "PROCESSING_INSTRUCTION";

        case XMLEvent.CHARACTERS:
            return "CHARACTERS";

        case XMLEvent.COMMENT:
            return "COMMENT";

        case XMLEvent.START_DOCUMENT:
            return "START_DOCUMENT";

        case XMLEvent.END_DOCUMENT:
            return "END_DOCUMENT";

        case XMLEvent.ENTITY_REFERENCE:
            return "ENTITY_REFERENCE";

        case XMLEvent.ATTRIBUTE:
            return "ATTRIBUTE";

        case XMLEvent.DTD:
            return "DTD";

        case XMLEvent.CDATA:
            return "CDATA";

        case XMLEvent.SPACE:
            return "SPACE";
    }
    return "UNKNOWN_EVENT_TYPE , " + eventType;
}

运行游标示例

  1. 要编译和运行游标示例,在终端窗口中,转到INSTALL_DIR/jaxp-version/samples/目录,并输入以下命令:
    javac stax/cursor/*.java
    
  2. 使用以下命令在BookCatalogue.xml文件上运行CursorParse示例。

    CursorParse将打印出BookCatalogue.xml文件的每个元素。

java stax/event/CursorParse stax/data/BookCatalogue.xml

游标到事件示例

CursorApproachEventObject.java位于tut-install/javaeetutorial5/examples/stax/cursor2event/目录中,演示了如何在使用游标API时获取XMLEvent对象返回的信息。

这里的思路是,游标API的XMLStreamReader返回与特定事件对应的整数常量,而事件迭代器API的XMLEventReader返回不可变和持久的事件对象。XMLStreamReader更高效,但XMLEventReader更易于使用,因为与特定事件相关的所有信息都封装在返回的XMLEvent对象中。但是,事件方法的缺点是为每个事件创建对象的额外开销,这会消耗时间和内存。

鉴于此,可以使用XMLEventAllocator将事件信息作为XMLEvent对象获取,即使在使用游标API时也可以。

实例化XMLEventAllocator

第一步是创建一个新的XMLInputFactory并实例化XMLEventAllocator

XMLInputFactory xmlif = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + xmlif);
xmlif.setEventAllocator(new XMLEventAllocatorImpl());
allocator = xmlif.getEventAllocator();
XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
                           new FileInputStream(filename));

创建事件迭代器

下一步是创建一个事件迭代器:

int eventType = xmlr.getEventType();

while (xmlr.hasNext()) {
    eventType = xmlr.next();
    // 将所有"Book"元素作为XMLEvent对象获取
    if (eventType == XMLStreamConstants.START_ELEMENT 
        && xmlr.getLocalName().equals("Book")) {
        // 获取不可变的XMLEvent
        StartElement event = getXMLEvent(xmlr).asStartElement();
        System.out.println ("EVENT: " + event.toString());
    }
}

创建分配器方法

最后一步是创建XMLEventAllocator方法:

private static XMLEvent getXMLEvent(XMLStreamReader reader)
    throws XMLStreamException {
    return allocator.allocate(reader);
}

运行光标到事件示例

  1. 要编译和运行光标到事件示例,在终端窗口中,进入INSTALL_DIR/jaxp-version/samples/目录,然后输入以下命令:
    javac -classpath ../lib/jaxp-ri.jar stax/cursor2event/*.java
    
  2. 使用以下命令在BookCatalogue.xml文件上运行CursorApproachEventObject示例。
    java stax/cursor2event/CursorApproachEventObject stax/data/BookCatalogue.xml
    

    CursorApproachEventObject将打印出BookCatalogue.xml文件定义的事件列表。

事件示例

INSTALL_DIR/jaxp-version/samples/stax/event/目录中,EventParse.java演示了如何使用StAX事件API读取XML文档。

创建输入工厂

第一步是创建一个XMLInputFactory的新实例:

XMLInputFactory factory = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + factory);

创建事件读取器

接下来是创建一个XMLEventReader的实例:

XMLEventReader r = factory.createXMLEventReader
                       (filename, new FileInputStream(filename));

创建事件迭代器

第三步是创建一个事件迭代器:

XMLEventReader r = factory.createXMLEventReader
                       (filename, new FileInputStream(filename));
while (r.hasNext()) {
    XMLEvent e = r.nextEvent();
    System.out.println(e.toString());
}

获取事件流

最后一步是获取底层事件流:

public final static String getEventTypeString(int eventType) {
    switch (eventType) {
        case XMLEvent.START_ELEMENT:
            return "START_ELEMENT";

        case XMLEvent.END_ELEMENT:
            return "END_ELEMENT";

        case XMLEvent.PROCESSING_INSTRUCTION:
            return "PROCESSING_INSTRUCTION";

        case XMLEvent.CHARACTERS:
            return "CHARACTERS";

        case XMLEvent.COMMENT:
            return "COMMENT";

        case XMLEvent.START_DOCUMENT:
            return "START_DOCUMENT";

        case XMLEvent.END_DOCUMENT:
            return "END_DOCUMENT";

        case XMLEvent.ENTITY_REFERENCE:
            return "ENTITY_REFERENCE";

        case XMLEvent.ATTRIBUTE:
            return "ATTRIBUTE";

        case XMLEvent.DTD:
            return "DTD";

        case XMLEvent.CDATA:
            return "CDATA";

        case XMLEvent.SPACE:
            return "SPACE";
    }
    return "UNKNOWN_EVENT_TYPE," + eventType;
}

返回输出

当运行事件示例时,EventParse类被编译,XML流被解析为事件并返回到STDOUT。例如,Author元素的实例被返回为:

<[’http://www.publishing.org’]::Author>
    Dhirendra Brahmachari
</[’http://www.publishing.org’]::Author>

注意在这个例子中,事件包含一个包含命名空间的开始和结束标签。元素的内容作为字符串在标签内返回。

类似地,Cost元素的实例被返回为:

<[’http://www.publishing.org’]::Cost currency=’INR’>
    11.50
</[’http://www.publishing.org’]::Cost

在这种情况下,currency属性和值在事件的开始标签中返回。

运行事件示例

  1. 要编译和运行事件示例,在终端窗口中,进入INSTALL_DIR/jaxp-version/samples/目录,然后输入以下命令:
    javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
    
  2. 使用以下命令在BookCatalogue.xml文件上运行EventParse示例。
    java stax/event/EventParse stax/data/BookCatalogue.xml
    

    EventParse将打印出由BookCatalogue.xml文件定义的所有元素的数据。

过滤器示例

位于INSTALL_DIR/jaxp-version/samples/stax/filter/目录中,MyStreamFilter.java演示了如何使用StAX流过滤器API来过滤应用程序不需要的事件。在此示例中,解析器会过滤掉除StartElementEndElement之外的所有事件。

实现StreamFilter类

MyStreamFilter类实现了javax.xml.stream.StreamFilter

public class MyStreamFilter implements javax.xml.stream.StreamFilter {
    // ...
}

创建Input Factory

下一步是创建XMLInputFactory的实例。在这种情况下,还在工厂上设置了各种属性:

XMLInputFactory xmlif = null ;

try {
    xmlif = XMLInputFactory.newInstance();
    xmlif.setProperty(
        XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
        Boolean.TRUE);

    xmlif.setProperty(
        XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
        Boolean.FALSE);

    xmlif.setProperty(
        XMLInputFactory.IS_NAMESPACE_AWARE,
        Boolean.TRUE);

    xmlif.setProperty(
        XMLInputFactory.IS_COALESCING,
        Boolean.TRUE);
} 
catch (Exception ex) {
    ex.printStackTrace();
}

System.out.println("FACTORY: " + xmlif);
System.out.println("filename = "+ filename);

创建过滤器

下一步是实例化一个文件输入流并创建流过滤器:

FileInputStream fis = new FileInputStream(filename);
XMLStreamReader xmlr = xmlif.createFilteredReader(
                           xmlif.createXMLStreamReader(fis), 
                           new MyStreamFilter());

int eventType = xmlr.getEventType();
printEventType(eventType);

while (xmlr.hasNext()) {
    eventType = xmlr.next();
    printEventType(eventType);
    printName(xmlr,eventType);
    printText(xmlr);

    if (xmlr.isStartElement()) {
        printAttributes(xmlr);
    }
    printPIData(xmlr);
    System.out.println("-----------------------");
}

捕获事件流

下一步是捕获事件流。这与事件示例中的方式基本相同。

过滤流

最后一步是过滤流:

public boolean accept(XMLStreamReader reader) {
    if (!reader.isStartElement() && !reader.isEndElement())
        return false;
    else
        return true;
}

返回输出

当你运行过滤器示例时,MyStreamFilter类将被编译,XML流将被解析为事件并返回给STDOUT。例如,一个Author事件将被返回如下:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Author
HAS NO TEXT
HAS NO ATTRIBUTES
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Author
HAS NO TEXT
-----------------------------

类似地,一个Cost事件将被返回如下:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Cost
HAS NO TEXT

HAS ATTRIBUTES:
 ATTRIBUTE-PREFIX:
 ATTRIBUTE-NAMESP: null
ATTRIBUTE-NAME:   currency
ATTRIBUTE-VALUE: USD
ATTRIBUTE-TYPE:  CDATA

-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Cost
HAS NO TEXT
-----------------------------

有关StAX事件解析的更详细讨论,请参阅迭代器API读取XML流

运行过滤器示例

  1. 要编译和运行过滤器示例,在终端窗口中,进入INSTALL_DIR/jaxp-version/samples/目录,并输入以下命令:
    javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
    
  2. 使用以下命令在BookCatalogue.xml文件上运行MyStreamFilter示例。此示例需要设置java.endorsed.dirs系统属性,指向samples/lib目录。
    java -Djava.endorsed.dirs=../lib stax/filter/MyStreamFilter -f stax/data/BookCatalogue.xml
    

    MyStreamFilter将按照BookCatalogue.xml文件定义的事件,将其作为XML流输出。

读取和写入示例

位于INSTALL_DIR/jaxp-version/samples/stax/readnwrite/目录中的EventProducerConsumer.java演示了如何同时将StAX解析器作为生产者和消费者使用。

StAX XMLEventWriter API扩展自XMLEventConsumer接口,被称为事件消费者。相比之下,XMLEventReader是一个事件生产者。StAX支持同时读取和写入,因此可以从一个XML流中顺序读取并同时写入到另一个流中。

读取和写入示例展示了如何使用StAX生产者/消费者机制同时读取和写入。此示例还展示了如何修改流以及如何动态添加新事件,然后将其写入到另一个流中。

创建事件生产者/消费者

第一步是实例化一个事件工厂,然后创建一个事件生产者/消费者的实例:

XMLEventFactory m_eventFactory = XMLEventFactory.newInstance();

public EventProducerConsumer() {
    // ...
    try {
        EventProducerConsumer ms = new EventProducerConsumer();
    
        XMLEventReader reader = XMLInputFactory.newInstance().
        createXMLEventReader(new java.io.FileInputStream(args[0]));
    
        XMLEventWriter writer = 
            XMLOutputFactory.newInstance().createXMLEventWriter(System.out);
    }
    // ...
}

创建迭代器

下一步是创建一个迭代器来解析流:

while (reader.hasNext()) {
    XMLEvent event = (XMLEvent)reader.next();
    if (event.getEventType() == event.CHARACTERS) {
        writer.add(ms.getNewCharactersEvent(event.asCharacters()));
    }
    else {
        writer.add(event);
    }
}
writer.flush();

创建写入器

最后一步是创建一个流写入器,以新的Character事件的形式:

Characters getNewCharactersEvent(Characters event) {
    if (event.getData().equalsIgnoreCase("Name1")) {
        return m_eventFactory.createCharacters(
                   Calendar.getInstance().getTime().toString());
    }
    // else return the same event
    else {
        return event;
    }
}

返回输出结果

当运行读取和写入示例时,EventProducerConsumer类将被编译,并将XML流解析为事件并写回到STDOUT。输出结果是示例XML文档中描述的BookCatalog.xml文件的内容。

运行读写示例

  1. 要编译并运行读写示例,在终端窗口中,进入INSTALL_DIR/jaxp-version/samples/目录,然后输入以下命令:
    javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java
    
  2. 使用以下命令在BookCatalogue.xml文件上运行EventProducerConsumer示例。
    java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml
    

    EventProducerConsumer将打印出BookCatalogue.xml文件的内容。

写入器示例

CursorWriter.java位于INSTALL_DIR/jaxp-version/samples/stax/writer/目录中,演示了如何使用StAX游标API编写XML流。

创建输出工厂

第一步是创建XMLOutputFactory的实例:

XMLOutputFactory xof =  XMLOutputFactory.newInstance();

创建流写入器

下一步是创建XMLStreamWriter的实例:

XMLStreamWriter xtw = null;

写入流

最后一步是写入XML流。注意,在写入最后一个EndDocument之后,流会被刷新和关闭:

xtw = xof.createXMLStreamWriter(new FileWriter(fileName));

xtw.writeComment("这里的所有元素都明确地位于HTML命名空间中");
xtw.writeStartDocument("utf-8","1.0");
xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","html");
xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "head");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "title");
xtw.writeCharacters("Frobnostication");
xtw.writeEndElement();
xtw.writeEndElement();

xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "body");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "p");
xtw.writeCharacters("Moved to");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "a");
xtw.writeAttribute("href","http://frob.com");
xtw.writeCharacters("here");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndDocument();

xtw.flush();
xtw.close();

返回输出

运行写入器示例时,CursorWriter类被编译,并且XML流被解析为事件并写入名为dist/CursorWriter-Output的文件中:

<!--这里的所有元素都明确在HTML命名空间中-->
<?xml version="1.0" encoding="utf-8"?>
<html:html xmlns:html="http://www.w3.org/TR/REC-html40">
<html:head>
<html:title>Frobnostication</html:title></html:head>
<html:body>
<html:p>转移到<html:a href="http://frob.com">这里</html:a>
</html:p>
</html:body>
</html:html>

在实际的dist/CursorWriter-Output文件中,该流没有任何换行符;这里添加换行是为了更容易阅读清单。在这个例子中,与事件示例中的对象流一样,命名空间前缀添加到了开放和关闭的HTML标签。在StAX规范中并不要求添加此前缀,但在输出流的最终范围不确定时,这是一种好的做法。

运行Writer示例

  1. 要编译和运行Writer示例,在终端窗口中,进入INSTALL_DIR/jaxp-version/samples/目录,然后输入以下命令:
    javac -classpath \
        ../lib/jaxp-ri.jar stax/writer/*.java
    
  2. 运行CursorWriter示例,指定输出文件的名称。
    java stax/writer/CursorWriter -f output_file
    

    CursorWriter将创建一个相应名称的输出文件,其中包含返回的输出中显示的数据。


上一页:Oracle 的流式 XML 解析器实现
下一页:更多信息