【问题标题】:Convert XML to Java with minimal memory usage以最少的内存使用将 XML 转换为 Java
【发布时间】:2021-03-31 23:53:59
【问题描述】:

我希望能够使用尽可能少的内存将大型 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>

【问题讨论】:

    标签: java xml xpath jaxb moxy


    【解决方案1】:

    我有一段时间没有使用 JAXB,但是无论选择什么解决方案,您都需要遍历 XML 文档来读取数据,隐式使用 DOM(然后可能是 XPath)或显式使用 SaX 之类的流 API(推送模型,通过回调获取数据)或StAX(拉模型,通过调用方法获取数据)。

    JAXB Users Guide 在“4.4. 处理大型文档”小节“4.4.1. 按块处理文档”中提供了以下信息。我在 Github 上添加了示例的链接。我这里没有空间来包含所有内容。

    这种XML适合块处理;主要思想是使用 StAX API,运行一个循环,并分别解组各个块。您的程序作用于单个块,然后将其丢弃。这样,您最多只能在内存中保留一个块,从而可以处理大型文档。

    有关如何执行此操作的更多信息,请参阅 JAXB RI 分发中的 streaming-unmarshalling examplepartial-unmarshalling example。流式解组示例的优点是它可以处理任意嵌套级别的块,但它需要您处理推送模型 --- JAXB 解组器会将新块“推送”给您,您需要正确处理它们在那里。

    相比之下,部分解组示例在拉模型中工作(这通常使处理更容易),但这种方法在数据绑定部分而不是重复部分有一些限制。

    pull-parserxml-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"
    

    【讨论】:

    • 是的,我已经查看了大多数选项,但它们并不是我真正想要的。 XML 文档可能包含大量书籍,而我可能只需要抓取最后一本书。所以我想节省时间和空间,只抓住那个,而不是遍历其余的。另一件事是,每本书也会有几个元素,所以它不仅仅是抓住一个字符串那么简单。
    • 如果您正在寻找的Document 中有数百行XML,那么我将值作为字符串获取的示例并不复杂。解组过程将处理它。我不明白你的意思。这就是为什么要使用解析器。关于您的第一点,我邀请您在得出结论之前使用流式 API 进行一些基准测试。即使您不使用 XML 解析器而只是手动执行,您仍然必须通过在文件中按顺序查找来找到正确的文档,除非您有其他信息(例如在这种情况下您没有的索引)
    • 或者使用像 BaseX 这样的原生 XML 数据库和 XQuery,或者根本不使用 XML。我的意思是如果文件太大并且您需要频繁访问,请使用关系数据库,例如,而不是 XML 文档。但在此之前,请先进行基准测试以清楚了解您的限制条件。
    猜你喜欢
    • 1970-01-01
    • 2015-04-11
    • 2010-10-27
    • 2014-01-05
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多