Java 如何通过StAX修改一个巨大的XML文件?

Java 如何通过StAX修改一个巨大的XML文件?,java,xml,xml-parsing,stax,Java,Xml,Xml Parsing,Stax,我有一个巨大的XML(~2GB),我需要添加新元素并修改旧元素。例如,我有: <books> <book>....</book> ... <book>....</book> </books> 但结果如下: <books> <book>....</book> .... <book>....</book> </books

我有一个巨大的XML(~2GB),我需要添加新元素并修改旧元素。例如,我有:

<books>
    <book>....</book>
    ...
    <book>....</book>
</books>
但结果如下:

<books>
   <book>....</book>
   ....
   <book>....</book>
</books><index></index>

....
....
....

有什么想法吗?

很清楚为什么它会这样做。实际上,您正在以输出附加模式打开现有文件,并在末尾写入元素。这显然与你想做的相矛盾

(旁白:考虑到输入端可能会看到输出端添加到文件末尾的元素,我很惊讶它的工作原理如此之好。事实上,像Evgeniy Dorofev的例子所给出的例外情况正是我所期望的。问题是,如果您试图同时读写一个文本文件,那么nd无论读写器使用任何形式的缓冲,无论是显式的还是隐式的,读写器都容易看到部分状态。)

要解决此问题,您必须从读取一个文件开始,然后写入另一个文件。追加将不起作用。然后,您必须安排将从输入文件读取的元素、属性、内容等复制到输出文件。最后,您需要在适当的位置添加额外的元素


是否有可能以类似RandomAccessFile的模式打开XML文件,但使用StAX方法写入

不,这在理论上是不可能的。为了能够以“随机”的方式浏览XML文件的结构文件,首先需要解析整个内容并建立所有元素所在的索引。即使这样,XML仍然作为文件中的字符存储,随机访问不允许插入和删除文件中间的字符。 也许您最好将XSL和SAX风格的解析器结合起来;例如,类似于这篇IBM文章的内容:

试试这个

    XMLInputFactory inFactory = XMLInputFactory.newInstance();
    XMLEventReader eventReader = inFactory.createXMLEventReader(new FileInputStream("1.xml"));
    XMLOutputFactory factory = XMLOutputFactory.newInstance();
    XMLEventWriter writer = factory.createXMLEventWriter(new FileWriter(file));
    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
    while (eventReader.hasNext()) {
        XMLEvent event = eventReader.nextEvent();
        writer.add(event);
        if (event.getEventType() == XMLEvent.START_ELEMENT) {
            if (event.asStartElement().getName().toString().equalsIgnoreCase("book")) {
                writer.add(eventFactory.createStartElement("", null, "index"));
                writer.add(eventFactory.createEndElement("", null, "index"));
            }
        }
    }
    writer.close();
注释

新的FileWriter(file,true)将附加到文件末尾,您几乎不需要它


equalsIgnoreCase(“书”)是个坏主意,因为XML是区分大小写的

也许JavaEE教程中的这个StAX读写示例有助于:


您可以在此处下载教程示例:

不幸的是,这段代码不起作用。NetBeans给了我一个错误:“原因:javax.xml.stream.XMLStreamException:ParseError at[row,col]:[4,2]消息:xml文档结构必须在同一实体内开始和结束。在com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.nextcom.sun.xml.internal.stream.XMLEventReaderImpl.nextEvent(XMLEventReaderImpl.java:83)上的…54 more'而且它删除了文件的所有内容…有什么异常吗?我在发布之前用您的xml测试了它。我刚刚尝试了它。同样的异常:'由:javax.xml.stream.XMLStreamException:ParseError at[row,col]:[3,5]消息:XML文档结构必须在同一实体内开始和结束。com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:598)在com.sun.XML.internal.stream.XMLEventReaderImpl.nextEvent(XMLEventReaderImpl.java:83)在library.controllers.BookCardController.saveToXML(BookCardController.java:138)…54 more“我真的不知道为什么,但另外,此代码会清除我的文件。嗯,似乎此异常是因为我使用了相同的文件作为输入和输出。在选择不同的目标文件后,代码开始工作,但…输出如下:“”。我需要将元素包含到现有XML中。如果仅保留y此XMLEvent event=eventReader.nextEvent();writer.add(event);在应该获得输出==输入的循环中,它不能丢失,请尝试调试
<books>
   <book>....</book>
   ....
   <book>....</book>
</books><index></index>
    XMLInputFactory inFactory = XMLInputFactory.newInstance();
    XMLEventReader eventReader = inFactory.createXMLEventReader(new FileInputStream("1.xml"));
    XMLOutputFactory factory = XMLOutputFactory.newInstance();
    XMLEventWriter writer = factory.createXMLEventWriter(new FileWriter(file));
    XMLEventFactory eventFactory = XMLEventFactory.newInstance();
    while (eventReader.hasNext()) {
        XMLEvent event = eventReader.nextEvent();
        writer.add(event);
        if (event.getEventType() == XMLEvent.START_ELEMENT) {
            if (event.asStartElement().getName().toString().equalsIgnoreCase("book")) {
                writer.add(eventFactory.createStartElement("", null, "index"));
                writer.add(eventFactory.createEndElement("", null, "index"));
            }
        }
    }
    writer.close();