使用StAX解析XML:使用定制事件和编写 XML

除了提供一个低层的基于指针的 API 之外,StAX 还提供了一个功能强大的基于迭代器的方法,它通过使用事件对象传送关于解析流的信息,以处理 XML。本系列的第 2 部分 详细研究了这种 API 并提供了一些使用它的例子。在本篇文章中,将介绍定制化技术,该技术使用由应用程序定义的事件,您还将看到如何创建定制事件类并使用它们结合基于事件迭代 器的 API 来处理 XML。最后(同样也是重要的一点),将回顾由 StAX 提供的可将 XML 编写为标记流和事件对象的序列化器 API。

创建定制事件

当 开发复杂应用程序时,使用分层方法(应用程序的下层为其上层提供必需的抽象)构建应用程序通常会很有用。例如,您可能会将处理 XML 的所有代码集合到一个应用程序提供的高层对象模型。这种技术不仅考虑到通用概念和解决方案的重用,并且还可以加速开发时间和产生更易维护的代码。

因为 StAX 所使用的基于拉的方法将应用程序置于解析过程的控制之下,所以您可以将解析过的事件转换为特定于应用程序的模型对象(例如私有消息或者其他结构的构建 块)。然而,您可能发现继续使用事件会更为方便,这样,您只需在 XML 内容中简单地创建定制事件就可以表示更复杂的结构。通过在底层 XML 数据结构之上叠加定制类型,就可以简化应用程序代码的开发同时允许低层仍然将这些类型作为事件对象处理(例如,将它们作为事件写入一个输出流)。

StAX 事件层次结构是可扩充的。可以扩展已有的事件和定义全新的事件类型。因为事件对象被定义为 Java™ 接口而不是类,因此您可以自由决定如何实现它们。例如,您可以将已有的对象模型分类并且将每一个类型表示为一个事件。您同样可以通过组装、委托等达到相同 的目的。清单 1 显示了一个扩展 Characters 事件来表示特定的数据类型(本例中是一个 Java Date )的值的例子。所有的子类将文本数据转换为数据值。注意,由于标准事件接口的公共实现不提供 StAX,您可以使用 Decorator 模式包装一个已有的 Characters 实例并向其委托所有的方法调用。


清单 1. 定制 Characters 事件的扩展来表示 Date 值

                
final DatatypeFactory tf = DatatypeFactory.newInstance();

class DateTime implements Characters {

  private final Characters d;
  private final Date value;

  DateTime(Characters d) {
    this.d = d;
    XMLGregorianCalendar cal = tf.newXMLGregorianCalendar(d.getData());
    value = cal.toGregorianCalendar().getTime();
  }

  Date getValue() { return value; }
  public Characters asCharacters() { return d.asCharacters(); }
  public EndElement asEndElement() { return d.asEndElement(); }
  public StartElement asStartElement() { return d.asStartElement(); }
  public String getData() { return d.getData(); }
  public int getEventType() { return d.getEventType(); }
  public Location getLocation() { return d.getLocation(); }
  public QName getSchemaType() { return d.getSchemaType(); }
  public boolean isAttribute() { return d.isAttribute(); }
  public boolean isCData() { return d.isCData(); }
  public boolean isCharacters() { return d.isCharacters(); }
  public boolean isEndDocument() { return d.isEndDocument(); }
  public boolean isEndElement() { return d.isEndElement(); }
  public boolean isEntityReference() { return d.isEntityReference(); }
  public boolean isIgnorableWhiteSpace() { return d.isIgnorableWhiteSpace(); }
  public boolean isNamespace() { return d.isNamespace(); }
  public boolean isProcessingInstruction() { return d.isProcessingInstruction(); 
}
  public boolean isStartDocument() { return d.isStartDocument(); }
  public boolean isStartElement() { return d.isStartElement(); }
  public boolean isWhiteSpace() { return d.isWhiteSpace(); }
  public void writeAsEncodedUnicode(Writer writer)
      throws XMLStreamException {
    d.writeAsEncodedUnicode(writer);
  }
}

 

通常新的事件被完全用来表示特定于应 用程序的 XML 结构。例如,通过一个定制事件类给出某些标记及其属性和/或文本内容,这个类为访问这种数据定义了一个 API(因此就使应用程序从必须使用 XML API 获取数据中解放出来)。即使一个定制事件由几个标准事件复合而成(如 StartElement 、Characters 和 EndElement ),它仍然是一个单独类型的事件,因为它不能被映射到这些事件中的任何一个。

XMLEvent 接口

任何定制事件类必须实现 XMLEvent 接口。在 StAX 的当前版本,没有可以通过扩展实现定制事件的抽象基类。幸运的是,您可以在 XMLEvent 中很轻松地实现大部分的方法。例如,向下转型方法(asStartElement() 、asEndElement() 和 asCharacters() )中的每一个都可以轻易地抛出一个 ClassCastException (因为这个定制事件不是其中的任何一个)。同样地,布尔查询方法,用于快速决定事件类型(isAttribute() 、isCharacters() 等),可以仅返回 false。如果一个定制事件由其他事件复合而成,getLocation() 方法可仅返回最后一个事件的位置。getSchemaType() 方法可以从相关联的 XML 模式返回外部事件的复合类型的名称。可以编写 writeAsEncodedUnicode(Writer) 方法来依次委派给每一个所包含的事件(例如,对于一个只有简单内容的代表所有元素的定制事件,首先要编写 StartElement ,然后是 Characters ,接着是 EndElement )。最后,getEventType() 必须返回一个在整个应用程序中表示新事件类型的惟一值。0 到 256 的值被保留下来供 StAX 提供者使用,所以您必须选择这个范围以外的值(并很好地加以说明,这样就不会与整个应用程序中使用的其他定制事件起冲突)。

清单 2 显示了一个定制事件例子,它展现一个 Atom 提要中的图标元素。这个元素由 Atom Syndication Format 规范(1.0 版本)定义,包含了提要的图标的 URL。


清单 2. 展现 Atom 提要图标(只含文本的元素)的定制事件

                
class Icon implements XMLEvent {

  private final StartElement startElement;
  private final String url;
  private final EndElement endElement;

  Icon(StartElement startElement, String url, EndElement endElement) {
    this.startElement = startElement;
    this.url = url;
    this.endElement = endElement;
  }

  String getURL() { return url; }
  public Characters asCharacters() { throw new ClassCastException(); }
  public EndElement asEndElement() { throw new ClassCastException(); }
  public StartElement asStartElement() { throw new ClassCastException(); }
  public int getEventType() { return 257; }
  public Location getLocation() { return endElement.getLocation(); }
  public QName getSchemaType() { return null; }
  public boolean isAttribute() { return false; }
  public boolean isCharacters() { return false; }
  public boolean isEndDocument() { return false; }
  public boolean isEndElement() { return false; }
  public boolean isEntityReference() { return false; }
  public boolean isNamespace() { return false; }
  public boolean isProcessingInstruction() { return false; }
  public boolean isStartDocument() { return false; }
  public boolean isStartElement() { return false; }
  public void writeAsEncodedUnicode(Writer writer)
      throws XMLStreamException {
    startElement.writeAsEncodedUnicode(writer);
    ef.createCharacters(url).writeAsEncodedUnicode(writer);
    endElement.writeAsEncodedUnicode(writer);
  }
}

 

到目前为止,您已经了解了如何定义定制事件类,但是还不清楚如何让 StAX 在解析过程中使用它们。接下来的章节介绍将定制事件类插入框架中的技术。


使用定制事件解析 XML

定义一个定制事件后,就可以在事件处理代码中使用它。您可以随意将任意的标准事件序列转化为定制事件(并将它传送给充当事件消费者的其他组件),与将事件转换为一个特定于应用程序对象模型所做的一样。但 StAX 为将定制事件实现插入到框架中提供了一个更通用的机制。

XMLEventAllocator API

正如您已了解的一样,在逻辑上基于事件迭代器的 API 是基于指针的 API 上面的一层。事实上,XMLEventReader 的一个标准实现可以包装一个 XMLStreamReader 并在其当前状态的基础上创建事件对象。为了支持应用程序定义的事件,当需要创建一个具体事件时,XMLEventReader 使用一个应用程序提供的 XMLEventAllocator 实例,这可以通过 XMLInputFactory 进行设置。要使用一个定制 XMLEventAllocator ,应用程序应该通过调用 XMLInputFactory 的 setEventAllocator(XMLEventAllocator) 方法提供它的一个实例。

XMLEventAllocator 本质上是基于指针的 API 和基于事件迭代器的 API 之间的一座桥梁。除了充当其自身的工厂(XMLInputFactory 为每一个新的 XMLEventReader 实例调用分配器的 newInstance() 方法)之外,该接口还定义了两个事件分配方法。要返回一个表示 stream reader 当前状态的 XMLEvent 需要使用 allocate(XMLStreamReader )方法,但该方法不能修改 stream reader 的当前状态。这个方法应该主要实现一个指针到事件对象的映射。另一个方法 allocate(XMLStreamReader, XMLEventConsumer) 应使用所提供的 stream reader 来创建一个或更多事件对象并将它们传递给所提供的事件消费者。应用程序在处理过程中可以随意修改 stream reader 的状态;例如,它可以将一个单独的状态分成多个事件对象,或相反地,将几个状态聚结为一个事件(比如通过读取一串标记并将它表示为一个事件对象)。

XMLEventAllocator 的使用是可选的,它没有默认的实例(就是说,默认情况下 XMLEventReader 使用创建事件对象的特定于实现的方法),这样有一些不方便,因为它意味着开发人员不能够扩展已有的分配器(比如通过装饰此分配器)而必须从头开始构建自己的分配器。不过,定制的 XMLEventAllocator 不需要提供每个标准事件类型自己的实现方法。它可以(甚至被鼓励这样做)使用 XMLEventFactory 来实现此目的。

清单 3 显示了一个简单分配器的实现,它将 Atom 提要元素 “published” 和 “updated” 的文本内容转换为一个 DateTime 事件(Characters 事件的扩展)。这些元素只包含文本内容,其文本应该代表一个日期/时间值。这种定制化由 allocate(XMLStreamReader, XMLEventConsumer) 方法执行;当遇到合适的 StartElement 时,所有元素的文本(一直到对应的 EndElement )都会被转换成单一的 Characters 事件,该事件反过来又由定制 DateTime 事件装饰。结果被馈送给事件消费者。


清单 3. 从元素的文本(其内容代表一个日期/事件值)创建 DateTime 事件的简单事件分配器

                
final String ATOM_NS = "http://www.w3.org/2005/Atom";
final QName PUBLISHED = new QName(ATOM_NS, "published");
final QName UPDATED = new QName(ATOM_NS, "updated");

final XMLEventFactory ef = XMLEventFactory.newInstance();

class CustomAllocator implements XMLEventAllocator {
	
  public XMLEvent allocate(XMLStreamReader r)
      throws XMLStreamException {
    switch (r.getEventType()) {
    case XMLStreamConstants.CDATA:
      return ef.createCData(r.getText());
    case XMLStreamConstants.CHARACTERS:
      if (r.isWhiteSpace())
        return ef.createSpace(r.getText());

      return ef.createCharacters(r.getText());
    case XMLStreamConstants.COMMENT:
      return ef.createComment(r.getText());
    case XMLStreamConstants.DTD:
      return ef.createDTD(r.getText());
    case XMLStreamConstants.END_DOCUMENT:
      return ef.createEndDocument();
    case XMLStreamConstants.END_ELEMENT:
      return ef.createEndElement(r.getName(), allocateNamespaces(r));
    case XMLStreamConstants.PROCESSING_INSTRUCTION:
      return ef.createProcessingInstruction(r.getPITarget(), r.getPIData());
    case XMLStreamConstants.SPACE:
      return ef.createIgnorableSpace(r.getText());
    case XMLStreamConstants.START_DOCUMENT:
      String encoding = r.getEncoding();
      String version = r.getVersion();
      if (encoding != null && version != null && 
r.standaloneSet())
        return ef.createStartDocument(encoding, version, r.isStandalone());

      if (encoding != null && version != null)
        return ef.createStartDocument(encoding, version);

      if (encoding != null)
        return ef.createStartDocument(encoding);

      return ef.createStartDocument();
    case XMLStreamConstants.START_ELEMENT:
      return ef.createStartElement(r.getName(), allocateAttributes(r), 
allocateNamespaces(r));
    default:
      return null;
    }
  }

  private Iterator allocateNamespaces(XMLStreamReader reader) {
    ArrayList namespaces = new ArrayList();
    for (int i = 0, n = reader.getNamespaceCount(); i < n; ++i) {
      Namespace namespace;
      String prefix = reader.getNamespacePrefix(i);
      String uri = reader.getNamespaceURI(i);
      if (prefix == null)
        namespace = ef.createNamespace(uri);
      else
        namespace = ef.createNamespace(prefix, uri);

      namespaces.add(namespace);
    }

    return namespaces.iterator();
  }

  private Iterator allocateAttributes(XMLStreamReader r) {
    ArrayList attributes = new ArrayList();
    for (int i = 0, n = r.getAttributeCount(); i < n; ++i)
      attributes.add(ef.createAttribute(r.getAttributeName(i), 
r.getAttributeValue(i)));

    return attributes.iterator();
  }

  public void allocate(XMLStreamReader reader,
      XMLEventConsumer consumer) throws XMLStreamException {
    XMLEvent event = allocate(reader);
    if (event != null) {
      consumer.add(event);
      if (event.isStartElement()) {
        QName name = event.asStartElement().getName();
        if (PUBLISHED.equals(name) || UPDATED.equals(name)) {
          String text = reader.getElementText();
          Characters delegate = ef.createCharacters(text);
          DateTime dateTime = new DateTime(delegate);
          consumer.add(dateTime);
        }
      }
    }
  }

  public XMLEventAllocator newInstance() {
    return new CustomAllocator();
  }
};

 

当输入工厂通过定制事件分配器建好之后,从其中创建的每一个事件读取器都将使用它来创建事件对象。应用程序可以像平常一样使用事件读取器,但这些定制事件应能出现在解析过的事件流中(参见清单 4)。


清单 4. 使用定制分配器迭代所创建的事件

                
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setEventAllocator(new CustomAllocator());
XMLEventReader r = factory.createXMLEventReader(uri, input);

try {
  while (r.hasNext()) {
    XMLEvent event = r.nextEvent();
    if (event instanceof DateTime)
      System.out.println(((DateTime) event).getValue());
  }
} finally {
  r.close();
}

 

除了可以为 XML 内容创建特定于应用程序的抽象,此方法还可以以更为节省内存的方式使用事件对象。默认情况下,XMLEventReader 将为它返回的每个事件创建一个新的实例。虽然对应用程序来说这可能比较方便 —— 它可以缓存事件,甚至在解析完成之后还可以使用它们 —— 但在资源受限制的环境中或者处理大型 XML 文档时,它可能会带来负面的性能影响。对于不缓存事件对象的应用程序来说,重用每个事件类型的单个实例而不是每次创建一个新的实例(这样很可能会导致频繁 的垃圾收集)可能更有效。

在第一次需要它的时候,这样一个静态的分配器可能会创建事件的每个类型,以后每次只 返回由新的信息更新过的相同实例。但应用程序却必须了解策略的变化并且相应地处理事件。例如,它必须要十分小心不能顺序拉出两个事件,也不能窥视事件,因 为如果这样将丢失先前返回的事件数据。

 

以 StAX 方式编写 XML

如 果不讨论 StAX 的序列化支持,任何对 StAX 的介绍都是不完整的。与输入端一样,输出 XML 也有两种 API。第一种是较低层次的 API,StAX 支持将 XML 编写为 底层的标记流而不需要确保输出是格式良好的(就是说,由应用程序负责在正确的时间调用正确的方法)。另一种较高层次的 API 支持将全部事件对象加到输出流中。

不管何种 API,应用程序都必须首先使用 XMLOutputFactory 创建适当的编写器对象 —— 使用低层 API 的 XMLStreamWriter 或使用基于事件对象 API 的 XMLEventWriter 。要获得默认的 XMLOutputFactory ,调用它的静态 getInstance() 方法(就像任何其他的 JAXP 工厂)。

XMLOutputFactory 支持几种类型的输出,这些输出实际上都是与 XMLInputFactory 支持的输入类型相对应的;除了标准的 Java I/O OutputStream 和 Writer ,同样也支持 JAXP Result 。

在 XMLStreamWriter 接口定义的方法和标准的 Java I/O DataOuputStream 中的方法大体相似,只不过前者针对 XML,后者针对的是简单 Java 数据类型。虽然这个编写器不会检查它的输入是否格式良好,但它的确能够确保 CHARACTERS 事件的数据和属性值被正确转义。

要开始编写一个 XML 文档,应用程序首先调用 writeStartDocument 方法的几种重载版本中的一种。其中一种版本具备显式的文档编码和 XML 版本,另一种只有编码,还有一种对编码和 XML 版本(分别为 UTF-8 和 1.0)使用默认值而不设置参数。对于具有文档类型声明(Document Type Declaration,DTD)的文档,应用程序可以调用 writeDTD(String) 并以整个 DTD production 为参数。

writeStartElement 方法的三个版本都可用于编写元素的开始标记。其中一个版本包括前缀、本地名称和名称空间 URI,此版本将前缀绑定到元素的开始标记创建的新的上下文中的名称空间。另一个版本只包含名称空间 URI 和本地名称;这个版本编写没有前缀的元素(假设此名称空间是上下文默认的名称空间)。最后一个版本只包含本地名称,该版本无论默认名称空间如何都会在其内 启动元素。 注意在上述所有情况下,都由用户负责写出名称空间属性(就是说,将一个前缀绑定到 URI 并不写出这个名称空间声明)。

除了这些方法,还有三个对应的 writeEmptyElement 方法用来编写空元素(就是说,不具备单独的关闭标记)。这些方法同时打开和关闭元素。

当编写 XML 元素时,应用程序负责管理它们的名称空间。StAX 为此提供了几种方法。您可以分别使用 setNamespaceContext(NamespaceContext) 和 getNamespaceContext() 设置并检索全部的名称空间上下文。注意设置一个名称空间的上下文不会写出名称空间声明 —— 它仅定义前缀绑定。这些在代码写 START_ELEMENT 事件时会被使用到。setPrefix(String, String) 方法可以在给定的上下文中将一个前缀绑定到名称空间 URI,getPrefix(String) 检索绑定到给定的 URI 的前缀。setDefaultNamespace(String) 可以在当前的上下文中设置默认的名称空间。

为了准确地写出一个名称空间声明,应用程序可以用一个前缀和一个 URI 调用 writeNamespace(String, String) ,或者调用 writeDefaultNamespace(String) 在当前元素上声明默认名称空间。

编写属性类似于编写元素开始标记,只是您必须同时提供属性值。三个 writeAttribute 方法具有与writeStartElement 方法类似的签名(除了为属性值增加的 String 参数)并且就名称空间而言遵循类似的规则。

除非您使用 writeEmptyElement 方法中的一种,否则为了关闭每个元素的开始标记,在它的内容写出后必须调用 writeEndElement() 。

您可以使用 writeCharacters 和 writeCData 方法编写文本内容。后者可以将文本包装在 CDATA 块中。为了更好地使用内存,还提供了 writeCharacters 的一个版本,它包含一个带有偏移量和长度(而不是 String 对象)的 char 缓冲器。

writeEntityRef 、writeProcessingInstruction 和 writeComment 方法可以分别编写实体引用、过程说明和注释。可调用 writeEndDocument() 方法标记文档的末尾。

典型的类似流的输出方法,flush 和 close ,可以分别用来促使编写器将任何缓存的数据写入底层输出和关闭编写器对象。注意关闭方法只释放编写器需要的资源,并不关闭底层的输出。


清单 5. 使用 XMLStreamWriter 编写一个简单的 XHTML 文档

                
final String XHTML_NS = "http://www.w3.org/1999/xhtml";
XMLOutputFactory f = XMLOutputFactory.newInstance();
XMLStreamWriter w = f.createXMLStreamWriter(System.out);
try {
      w.writeStartDocument();
      w.writeCharacters("\n");
      w.writeDTD("<!DOCTYPE html " +
                  "PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
                  "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
      w.writeCharacters("\n");
      w.writeStartElement(XHTML_NS, "html");
      w.writeDefaultNamespace(XHTML_NS);
      w.writeAttribute("lang", "en");
      w.writeCharacters("\n");
      w.writeStartElement(XHTML_NS, "head");
      w.writeStartElement(XHTML_NS, "title");
      w.writeCharacters("Test");
      w.writeEndElement();
      w.writeEndElement();
      w.writeCharacters("\n");
      w.writeStartElement(XHTML_NS, "body");
      w.writeCharacters("This is a test.");
      w.writeEndElement();
      w.writeEndElement();
      w.writeEndDocument();
} finally {
      w.close();
}

 

与低层 API 相比,XMLEventWriter 使用的基于事件对象的序列化器 API 由更少的方法组成。与其对应的输入端 XMLEventReader 一样,这个编写器使用事件对象表示底层 XML InfoSet 的各个部分。典型的应用程序只需使用 add(XMLEvent) 方法就可写出整个文档。add(XMLEventReader) 是一个很方便的方法,用它可以写出从读取器获得的全部事件。使用这个方法,应用程序可以将整个 XML 流的内容有效地输送到另一个 XML 流,而且内容保持不变。

管理名称空间的一组方法和 XMLStreamWriter 中定义的方法是等价的。getNamespaceContext() 和 setNamespaceContext(NamespaceContext) 方法提供了对整个名称空间上下文的访问。setPrefix(String, String) 和 getPrefix(String) 可以分别绑定和获得名称空间的前缀绑定。最后,setDefaultNamespace(String) 方法可以为当前的名称空间上下文设置默认的名称空间。

正如 XMLStreamWriter 一样,该编写器提供了 flush() 和 close() 方法,可分别用于将缓存数据放到底层输出和关闭编写器。


清单 6. 使用 XMLEventWriter 编写一个简单的 XHTML 文档

                
final String XHTML_NS = "http://www.w3.org/1999/xhtml";
final QName HTML_TAG = new QName(XHTML_NS, "html");
final QName HEAD_TAG = new QName(XHTML_NS, "head");
final QName TITLE_TAG = new QName(XHTML_NS, "title");
final QName BODY_TAG = new QName(XHTML_NS, "body");

XMLOutputFactory f = XMLOutputFactory.newInstance();
XMLEventWriter w = f.createXMLEventWriter(System.out);
XMLEventFactory ef = XMLEventFactory.newInstance();
try {
      w.add(ef.createStartDocument());
      w.add(ef.createIgnorableSpace("\n"));
      w.add(ef.createDTD("<!DOCTYPE html " +
                  "PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
                  "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"));
      w.add(ef.createIgnorableSpace("\n"));
      w.add(ef.createStartElement(HTML_TAG, null, null));
      w.add(ef.createNamespace(XHTML_NS));
      w.add(ef.createAttribute("lang", "en"));
      w.add(ef.createIgnorableSpace("\n"));
      w.add(ef.createStartElement(HEAD_TAG, null, null));
      w.add(ef.createStartElement(TITLE_TAG, null, null));
      w.add(ef.createCharacters("Test"));
      w.add(ef.createEndElement(TITLE_TAG, null));
      w.add(ef.createEndElement(HEAD_TAG, null));
      w.add(ef.createIgnorableSpace("\n"));
      w.add(ef.createStartElement(BODY_TAG, null, null));
      w.add(ef.createCharacters("This is a test."));
      w.add(ef.createEndElement(BODY_TAG, null));
      w.add(ef.createEndElement(HTML_TAG, null));
      w.add(ef.createEndDocument());
} finally {
      w.close();
}



结束语

本 系列向您介绍了 StAX 以及如何在一些需要读/写(或两者都需要)XML 的应用程序中使用它。将 XML 流表示为一系列事件对象的做法功能十分强大,可同时提供灵活性和高效性。StAX 将成为 Java Standard Edition 下一版本的一部分,每个 Java 开发人员都会最终使用到它。

StAX处理XML 1.StAX(The Streaming API for XML),是一种利用拉模式解析(pull-parsing)XML文档的API。StAX 把重点放在流上,它提供了两套处理 XML 的 API:基于指针的 API, 把 XML 文档当做一个标记(或事件)流来处理;允许应用程序检查解析器的状态,获得解析的上一个标记的信息,然后再处理下一个标记,依此类推;另一种较为高级的是基于迭代器的 API,把 XML 作为一系列事件对象来处理,每个对象和应用程序交换 XML 结构的一部分。应用程序根据需要定制解 阅读详情

相关推荐

典!浅谈读写大型XML文件的流式API:StAX

为了克服SAX和DOM所存在的限制,JAXP随后引入了StAX(Streaming API for XML)。StAX巧妙地融合了SAX的事件驱动模型和DOM的编程便利性,实现了对海量XML数据的高性能读写解析。

weixin_50811484的博客 1934

使用 StAX 解析 XML,第 3 部分: 使用定制事件和编写 XML

定义定制事件并使用 StAX 的序列化器 API Peter Nehrer(pnehrer@ecliptical.ca), 自由撰稿人, 独立咨询顾问 简介:除了提供一个低层的基于指针的 API 之外,StAX 还提供了一个功能强大的基于迭代器的方法,它通过使用事件对象传送关于解析流的信息,以处理 XML。本系列的第 2 部分详细研究了这种 API...

helpbs 174

大模型和任务型对话系统如何结合

三种方法都是基于prompt1.prompt中定义大量可分类的domain标签,每种标签含有的样例。走对话系统nlu常规步骤,domain分类,意图分类,slot提取等。缺点:需要很复杂的prompt,写的未必完善。需要多次调用llm2.prompt中定义每种query下可以调用的functioncall。3.也是基于prompt,可以管理对话主题,实现更丰富的话题流转。面向某个具体领域,没有细分的domain类别。

qq_29837229的博客 2453

java stax_XML编程总结(五)——使用StAX接口操作xml

1 public classStAXTest {2 privateInputStream is;34 @Before5 public void setUp() throwsException{6 is=StAXTest.class.getClassLoader()7 .getResourceAsStream("books.xml");...

weixin_39587238的博客 174

Colmap中 patchmatch源码

惊世骇俗的Colmap PatchMatch源码 500多行,简明易懂 先了解PatchMatch的options: max_image_size: 任一维度的最大图像尺寸 gpu_index: gpu的下标 depth_min,depth_max: 随机采样的深度范围 window_radius:用于计算NCC(两图片的归一化互相关),NCC的计算需要用到 win_size. 图像匹配 | NCC 归一化互相关损失 | 代码 + 讲解 - 知乎 (zhihu.com) window_step: 用.

qq_24884193的博客 2211

ArcGIS License Server点击启动没反应,许可管理器无法启动怎么办

4 当我们再打开ArcGIS License Server Administrator的时候。3 在【服务】中找到【ArcGIS License Manager】双击。无法启动ArcGIS License Server的情况。在使用ArcGIS的过程中,我们经常会遇到。专注GIS领域知识分享,交流行业软件技巧。欢迎关注,不定期分享GIS各种问题和技巧。1 右击我的电脑——【管理】服务状态——启动,确定即可。启动类型选择——自动。

GIS蒲公英的博客 2万+

XML入门(四)使用STAX解析XML

前言       本章讲解使用STAX解析XML文件 方法 1.概念 在我们XML解析技术发展的过程中,诞生了很多优秀的解析技术,如:DOM、SAX、JDOM、Dom4J、STAX等 这里我们来讲解一下STAX解析XML文档的方法! 2.使用STAX解析XML 1)准备如下xml文件放在src目录下 &lt;?xml version="1.0" encoding="UTF-8"?...

jwang的博客 1315

OpenManus本地部署实战:从源码编译到浏览器路径优化全解析

本文详细解析了OpenManus本地部署的全过程,从源码编译到浏览器路径优化。涵盖环境准备、源码获取、模型配置、依赖安装等关键步骤,特别针对Windows系统下的chrome.exe路径修改提供了实用解决方案,帮助开发者高效完成部署并优化性能。

weixin_28826545的博客 381

JAVA解析XML(StAX)

HostAliveTask repeat 180 WebpageRespTask repeat 180 AlarmNumTask daily 16:45:00 import global.Global; import java.io.FileInputStream; import java.io.FileNotFoundEx

m_vptr的专栏 3986

PS/2鼠标接口协议

一、PS/2鼠标接口        PS/2接口用于许多现代的鼠标和键盘,由IBM最初开发和使用。物理上的PS/2接口有两种类型的连接器:5脚的DIN和6脚的MINI-DIN。        二、鼠标的接口协议原理             PS/2鼠标接口采用一种双向同步串行协议。即每在时钟线上发一个脉冲,就在数据线上发送一位数据。但是在相互传输中 ,主机是拥有总线控制权的

Ant's home 1万+

Yakit实战:利用验证码识别插件突破Webfuzzer自动化测试瓶颈

本文详细介绍了如何利用Yakit的Webfuzzer模块结合验证码识别插件,突破自动化安全测试中的验证码瓶颈。通过实战演示,讲解了如何部署本地OCR服务、编写热加载脚本,实现从获取验证码到自动识别并替换的全流程自动化,从而让Webfuzzer能够高效、无人值守地进行大规模密码喷洒等测试。

weixin_29323493的博客 283

stax解析xml_使用StAX部分解析XML文档

解析XML文档时, XMLEventReader实例通过其next()方法将事件对象传递给客户端应用程序-文档中每个语法单元一个。 但是,应用程序并不总是对接收所有事件类感兴趣。 仅查看XML元素及其属性的应用程序不关心表示注释或处理指令的事件。 幸运的是,StAX允许您通过实现事件过滤器来跳过某些事件类。 清单1显示了一个跳过所有XML处理指令的事件过滤器。 这些事件不会传递给事件读取器...

cuyi7076的博客 609

【OpenClaw从入门到精通】第35篇:电商“龙虾”掘金记——用OpenClaw实现小红书/抖音/淘宝全自动矩阵运营(2026实测版)

2026年,OpenClaw智能体已成为电商运营的“效率革命工具”,其多平台自动化能力让单人可完成传统3-5人的工作量。本文基于真实商业案例(来源:海报新闻、阿里云开发者社区等),系统拆解OpenClaw在小红书、抖音、淘宝的全链路运营方案:从阿里云部署搭建基础环境,到三大平台核心技能安装配置,再到跨平台矩阵工作流落地;同时揭秘代部署、模板售卖、轻定制、内容套利四大变现模式,并详解Token成本控制、安全风险规避等关键问题。文中所有代码均经过2026年3月实测验证,提供完整可复现的技术方案,帮助读者快速搭建

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2087

使用StAX解析器解析大型xml文件

本文大部分摘抄于IBM developerworks(主要是理论),详下面三篇文章,摘抄主要是为了使自己理解更深一点儿,仅当作笔记而已...也是为了以后再次使用时有个参考!摘抄并不全面,原文内容要丰富地多,详见原文。参考文章:使用 StAX 解析 XML,第 1 部分: Streaming API for XML (StAX) 简介:http://www.ibm.com/developerworks/cn/xml/x-stax1.html。

Yinhui0083的博客 1138

足式机器人全身 MPPI: 利用 MPPI 对足式机器人进行实时全身控制

本研究首次在真实四足机器人上成功部署了基于全身采样的模型预测控制(MPC)系统。采用模型预测路径积分(MPPI)算法,通过并行采样和评估控制轨迹实现实时运动规划。该系统能处理复杂接触场景,完成推箱子、爬越障碍及崎岖地形行走等任务。实验使用Unitree Go1机器人,结合MuJoCo仿真器快速生成运动策略,仅需简单控制策略即可实现稳健操作。硬件验证表明,该方法能有效完成目标导向的箱子推动任务。该成果标志着采样MPC在足式机器人实际应用中的重要突破。

2506_90492529的博客 1000

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

java的XML解析——StAX PULL解析

本文要点是: StudentPullUtils类,实现的两个方法示例, 一个用于将xml文件中的student解析到List&lt;Student&gt;中, 一个用于将List&lt;Student&gt;中的数据序列化到指定的xml文件中。 转载自:https://www.cnblogs.com/dqrcsc/p/4634074.html建议看原文,格式清晰。 ~~~~~~~~~~~...

HD243608836的博客 802

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

StAX-基于流的拉式XML解析

最近在学习webservice时,发现很多框架,技术都在使用StAX作为底层XML解析工具,于是今天看了看资料,学习了下,在这里做个总结。 简介 StAX,全称Streaming API for XML,一种全新的,基于流的JAVA XML解析标准类库。其最终版本于 2004 年 3 月发布,并成为了 JAXP 1.4(将包含在即将发布的 Java 6 中)的一部分。在某种程度上来说,StAX

xiongqi215的专栏 1573

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

XML解析--Stax

XML解析–Stax Streaming API for XML – 流模型中的拉模型 (Pull) – 在遍历文档时,会把感兴趣的部分从读取器中拉出,不需要引发事件,允许我们选择性地处理节点。这大大提高了灵活性,以及整体效率。 – 两套处理API - 基于指针的API,XMLStreamReader - 基于迭代器的API,XMLEventReader XML解析–Stax实例 books...

Torey_Li的博客 370

XML编程总结——使用StAX接口操作xml

使用StAX接口操作xml StAX,全称Streaming API for XML,一种全新的,基于流的JAVA XML解析标准类库。其最终版本于 2004 年 3 月发布,并成为了 JAXP 1.4(将包含在即将发布的 Java 6 中)的一部分。在某种程度上来说,StAX与SAX一样是基于XML事件的解析方式,它们都不会一次性加载整个XML文件。但是它们之间也有很大的不同。 StAX和SAX的区别——拉式解析器和推式解析器的区别 虽然StAX与SAX一样基于XML事件解析,相比于DOM将整个XM

David_XM的博客 917

使用StAX事件定义解析大XML

首先StAX的普及知识可以参考一下IBM Developement的下面这个系列文章   http://www.ibm.com/developerworks/cn/xml/x-stax1.html http://www.ibm.com/developerworks/cn/xml/x-stax2.html http://www.ibm.com/developerworks/cn/xm...

小野工作室 282

使用 StAX 解析 XML

StAX 概述 从一开始,Java API for XML Processing (JAXP) 就提供了两种方法来处理 XML:文档对象模型(DOM)方法是用标准的对象模型表示 XML 文档;Simple API for XML (SAX) 方法使用应用程序提供的事件处理程序来处理 XML。JSR-173 提出了一种面向流的新方法:Streaming API for XML (StAX)。其最终版...

Java天堂 203

java stax_Java学习:使用StAX解析XML

本文是我在学习《Java核心技术》第九版卷II(高级特性)时写的一段代码。原书作者为(美)Cay S. Horstmann,Gary Cornell,译者为陈昊鹏、王浩、姚建平等。我的Java版本为1.8Java提供了两种XML解析器:树型解释器DOM(Document Object Model,文档对象模型),和流机制解析器SAX(Simple API for XML,XML简单API)。流机制...

weixin_39577908的博客 348

stax 和jaxb 关系_XML解组基准:JAXB,STAx,Woodstox

stax 和jaxb 关系 介绍 上周末,我开始考虑如何以一种资源友好的方式处理大量XML数据。我要解决的主要问题是如何以块的形式处理大型XML文件,同时提供上游/下游系统,需要处理一些数据。 当然,我已经使用JAXB技术已有几年了。 使用JAXB的主要优点是可以加快产品上市时间; 如果拥有XML模式,则可以使用工具自动自动生成相应的Java域模型类(Eclipse Indigo,各种工...

最佳 Java 编程 684

java xml stax_Java StAX解析器示例以读取XML文件

java xml staxWe can use Java StAX parser to read XML file. Java Streaming API for XML (Java StAX) provides implementation for processing XML in java. StAX consists of two sets of API – cursor based AP...

从零开始的教程世界 550
上一篇: 使用StAX解析XML: 拉式解析和事件
下一篇: 《程序员》2007第十期算法擂台之JAVA解法
JavaSxy
博客等级 码龄21年 1粉丝 25原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值