如何解决以最少的内存使用量将 XML 转换为 Java
我希望能够使用尽可能少的内存将大型 XML 文件中的一小部分数据转换为 Java。例如,在下面的代码中,我希望能够从 XML 中提取 id='3' 的文档及其属性,而无需遍历其他文档。我可以单独使用 JAXB 来做到这一点吗?我是否需要结合使用 XPath 和 JAXB?我应该使用 JAXB MOXy 吗?
<Example id="10" date="1970-01-01" version="1.0">
<Properties>...</Properties>
<Summary>...</Summary>
<Document id="1">...</Document>
<Document id="2">...</Document>
<Document id="3">...</Document>
</Example>
解决方法
我有一段时间没有使用 JAXB,但无论选择什么解决方案,您都需要遍历 XML 文档以读取数据,隐式使用 DOM(然后可能是 XPath)或显式使用 SaX 等流 API(推送模型,您通过回调获取数据)或 StAX(拉模型,您通过调用方法获取数据)。
JAXB Users Guide 在“4.4. 处理大文档”部分,“4.4.1. 按块处理文档”小节中给出了以下信息。我在 Github 上添加了示例链接。我这里没有足够的空间来包含所有内容。
这种XML适用于chunk-processing;主要思想是使用 StAX API,运行一个循环,并分别解组单个块。您的程序作用于单个块,然后将其丢弃。这样,您最多只能在内存中保留一个块,这样您就可以处理大型文档。
有关如何执行此操作的更多信息,请参阅 JAXB RI 发行版中的 streaming-unmarshalling example 和 partial-unmarshalling example。流式解组示例的优点是它可以在任意嵌套级别处理块,但它需要您处理推送模型 --- JAXB 解组器会将新块“推送”给您,您需要正确处理它们
相比之下,部分解组示例在拉模型中工作(这通常使处理更容易),但这种方法在数据绑定部分而不是重复部分有一些限制。
pull-parser
和 xml-channel
示例看起来也很有希望。我根据 pull-parser
中的简单代码制作了一个 MWE。实现位于 Eclipse Implementation of JAXB。
$ cat source.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Example id="10" date="1970-01-01" version="1.0">
<Properties>properties</Properties>
<Summary>summary</Summary>
<Document id="1">one</Document>
<Document id="2">two</Document>
<Document id="3">three</Document>
</Example>
$ cat document.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Document">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="id" type="xs:int"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>
$ cat Main.java
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import java.io.FileReader;
import javax.xml.stream.*;
import static javax.xml.stream.XMLStreamConstants.*;
import generated.Document;
public class Main {
public static void main(String[] args) throws Exception {
int id = 3;
JAXBContext jaxbContext = JAXBContext.newInstance("generated");
Unmarshaller um = jaxbContext.createUnmarshaller();
XMLInputFactory xmlif = XMLInputFactory.newInstance();
XMLStreamReader xmlr = xmlif.createXMLStreamReader(new FileReader("source.xml"));
int event;
while (true) {
event = xmlr.next();
if (event == END_DOCUMENT) break;
if (event == START_ELEMENT && xmlr.getName().getLocalPart().equals("Document") && xmlr.getAttributeValue(null,"id").equals("3")) {
Document document = (Document) um.unmarshal(xmlr);
System.out.printf("Text is \"%s\"\n",document.getValue());
}
}
}
}
$ java --version
openjdk 15.0.1 2020-10-20
OpenJDK Runtime Environment (build 15.0.1+9-18)
OpenJDK 64-Bit Server VM (build 15.0.1+9-18,mixed mode,sharing)
$ wget https://repo1.maven.org/maven2/com/sun/xml/bind/jaxb-ri/3.0.0/jaxb-ri-3.0.0.zip
$ unzip jaxb-ri-3.0.0.zip
$ export PATH=`pwd`/jaxb-ri/bin:$PATH
$ export CLASSPATH=.:jaxb-ri/mod/*
$ xjc.sh document.xsd
Java major version: 15
parsing a schema...
compiling a schema...
generated/Document.java
generated/ObjectFactory.java
$ javac generated/*.java
$ javac Main.java
$ java Main
Text is "three"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。