【问题标题】:How to append a child element to a large XML file using Java?如何使用 Java 将子元素附加到大型 XML 文件?
【发布时间】:2012-03-09 18:12:53
【问题描述】:

我正在尝试使用 Java 创建一个 XML 文件,它是 GPS 坐标 (GPX) 的集合。每次我从我的 android 设备收到坐标(大约每秒 1 个)时,我都需要将结果附加到现有的 XML 文件中。我正在寻找的输出如下所示,其中 trkpt 元素作为重复项。问题是我不能只将新的 trkpt 添加到文件末尾,因为它需要在 trkseg 父元素内。

到目前为止,我已经尝试了两种不同的 API,SIMPLEXML 和 JDOM。使用 SIMPLEXML,我无法弄清楚如何将子元素附加到现有文件中,因此我切换到了 JDOM。 JDOM 允许我附加 trkpt 元素,如下所示,但是随着文件开始增长,它很快就减慢了程序的用户界面。使用 JDOM,我使用 SAXBuilder 重新打开文件并追加。我认为这个问题是它必须在添加新元素并重写文件之前在内存中复制整个文件。因此,文件越大,设备上的操作要求就越高。我需要一个在写入新数据之前不检查/复制整个文件的解决方案。有没有更有效的方法可以使用 Java 或 Java 的 API 来完成此任务?感谢您的帮助!

<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1">
        <trk>
            <trkseg>
                <trkpt lon="9.860624216140083" lat="54.9328621088893">
                    <ele>228.0</ele>
                </trkpt>
                <trkpt lon="9.860624216140100" lat="54.9328621088754">
                    <ele>234.0</ele>
                </trkpt>
                <trkpt lon="9.860624216140343" lat="54.9328621088678">
                    <ele>227.0</ele>
                </trkpt>
            </trkseg>
        </trk>
</gpx>

【问题讨论】:

  • 您是否总是以相同的方式附加数据?然后考虑逐行读取文件并以“与 XML 无关”的方式附加您的行;只要结果仍然是有效的 XML。
  • 您是否能够改为将数据附加到纯文件并稍后将整个文件转换为 XML?或者也许分批转换它?尝试不断更新 XML 似乎让自己的生活变得不必要地困难! ;-)
  • 我想您的问题与客户端处理(android)有关,而不是服务器端处理,对吗?
  • @The Nail,我目前每次编辑文件时都以相同的方式附加数据。
  • @DNA 我喜欢你的建议。稍后我将在我的服务器上使用这些数据,届时可以将其处理为 XML。如果一切都失败了,那可能就是我要走的路。

标签: java android xml api gpx


【解决方案1】:

在 I/O 方面总是存在瓶颈,尤其是在以重复方式打开/关闭/重新打开文件时。

DOM 处理程序每​​次打开文件时都会创建一个完整的树结构,但在更改该树时非常有效。

首先,您真的需要在每一次交易时打开、更改、保存文件吗?如果不是,则将文件的 DOM 保存在内存中,并通过对 XML 的引用进行更改。在用户退出应用或离开视图时保存。

如果您确实需要在每次滴答时保存文件,您仍然可以将 DOM 保留在内存中,并且只在每次滴答时将其保存到磁盘。

如果您需要在每次滴答时打开/保存/重新打开文件,请不要使用任何 XML 库 - 只需使用标准 FileWriter 或类似工具,并手动更改内容 - 但它仍然会如果文件变得非常大,则很难保持性能。

【讨论】:

  • 我不一定需要在每个刻度上都保存到文件中。只要在等待写入时丢失数据的可能性不大。我想我应该提到数据量可能会进入 GB 范围,因此在退出应用程序时保存可能是个问题。
  • @Ferrari692 是的,你也不应该在内存中保留那个大小的 DOM 对象。考虑使用其他方法而不是解析该大小的整个文件,无论是块还是非 XML。
【解决方案2】:

这听起来像是 SAX 的完美应用程序(在包 org.xml.sax 中找到它);它是用于 XML 访问和操作的流式 API。 SAX 为它遇到的每个元素生成事件,允许您将文件复制到新文件,而无需将其解析为大型内存树。当您到达输入文件的末尾时,只需在处理&lt;trkseg&gt; 的结束标记之前适当地附加新元素。

当然,您每秒重写此文件的方法本身就值得怀疑。您可以将信息捆绑在更大的部分中吗?您可以将信息转储到单个文件中,并以特定间隔(每 10/30/60 秒)将它们收集到单个文件中。

【讨论】:

  • 我不会有捆绑信息和使用更大间隔的问题。我会考虑使用 SAX。
【解决方案3】:

如果它像这里所说的那么简单,您可以使用 RandomAccessFile 并查找文件长度减去几个字节(就在根结束标记之前),然后开始覆盖那里。

【讨论】:

  • 有趣。我将不得不尝试一下。
【解决方案4】:

我建议将 xml 文件分成 3 个部分。

head.xml

<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1">
    <trk>
        <trkseg>

body.xml

<trkpt lon="9.860624216140083" lat="54.9328621088893">
    <ele>228.0</ele>
</trkpt>
<trkpt lon="9.860624216140100" lat="54.9328621088754">
    <ele>234.0</ele>
</trkpt>
<trkpt lon="9.860624216140343" lat="54.9328621088678">
    <ele>227.0</ele>
</trkpt>

tail.xml

        </trkseg>
    </trk>
</gpx>

现在每当您获得新数据时,只需将其附加到 body.xml

读取 xml 文件使用 SequenceInputStream 如下:

List<InputStream> list = new ArrayList<InputStream>(3);
list.add(new FileInputStream("head.xml"));
list.add(new FileInputStream("body.xml"));
list.add(new FileInputStream("tail.xml"));
InputStream xmlStream = new SequentialInputStream(Collections.enumeration(list));

【讨论】:

    猜你喜欢
    • 2016-10-05
    • 1970-01-01
    • 2011-10-05
    • 2022-10-15
    • 2013-01-04
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多