java - 以最少的内存使用将 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 用户指南在“4.4. 处理大型文档”部分的“4.4.1. 按块处理文档”小节中提供了以下信息。我在 Github 上添加了示例的链接。我这里没有空间来包含所有内容。
这种 XML 适用于块处理;主要思想是使用 StAX API,运行一个循环,并分别解组各个块。您的程序作用于单个块,然后将其丢弃。这样,您最多只能在内存中保留一个块,这使您可以处理大型文档。
有关如何执行此操作的更多信息,请参阅 JAXB RI 分发中的流式解组示例和部分解组示例。流式解组示例的优势在于它可以处理任意嵌套级别的块,但它需要您处理推送模型 --- JAXB 解组器会将新块“推送”给您,您需要正确处理它们那里。
相比之下,部分解组示例在拉模型中工作(这通常使处理更容易),但这种方法在数据绑定部分(重复部分除外)方面存在一些限制。
和示例看起来也很有希望pull-parser
。xml-channel
我根据pull-parser
. 该实现可在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"
推荐阅读
- string - 如何以特定数字访问字符串中的字符?
- go - 将 json 解组为类型
- python - 在 Pandas 中的分组数据帧中减去值
- java - ArrayIndexOutOfBounds 异常,但没有越界?
- multithreading - 从 Assembly 中的不同线程访问全局变量
- kubernetes - 从命令行更新 Kubernetes 密钥的最佳方法
- javascript - Getorgchart 中的鼠标悬停事件
- ms-access - MS Access 无效使用 Null(错误 94)
- vba - VBA/VB6 无法识别 VbCrLf
- java - 如何在通过 Helm 部署的 Java Spring Boot 应用程序中使用配置映射中的属性