使用 StAX 保留 XML 布局(属性顺序、换行符)以进行小的更改(例如更改属性)

Preserve XML layout (attribute order, newlines) using StAX to make small changes (e.g. change an attribute)

我正在尝试使用 StAX 迭代器替换 SVG 文件中某些属性的值 API。我使用 XMLEventReader 读取原始文件,检查和修改元素,然后写入 XMLEventWriter.

我的原始文件结构如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
...
-->
<!DOCTYPE ...
...
]>
<svg ...

我得到的输出不一样:

<?xml version="1.0"?><!--
...
--><!DOCTYPE ...
...
]><svg ...

如您所见,encoding 以及注释和文档类型周围的换行符都消失了。

此外,结果文件中所有标签的所有属性的顺序似乎是随机的。我读过 并且我知道不能保证属性顺序,但这对我没有帮助。

这些 SVG 文件在 Git 下,所以我想尽可能地保留它们的纯文本布局。

如何解决这些问题?对于我当前的任务,我可以将属性值替换为纯文本,无需任何解析,但我希望有一个解决方案可以让我考虑标签嵌套和类似的事情。

如果 StAX 无法完成,我完全愿意接受不同的方法。我已经尝试过 DOM 方法,但情况更糟。也许有一些 3d 方解析器...

VTD-XML(我是作者的一个开源项目)是java API在导出层级结构的同时解析后保留底层字节XML 树...这意味着您可以就地替换字节的任何部分,而无需对文档的不相关部分进行任何不必要的摆弄..甚至直接覆盖字节...零开销

在涉及更新属性的情况下,最好的选择不是使用 XMLEventWriter,而是在 XML 文件中查找标签的位置(字符偏移)并进行子字符串替换。你可以这样做:

  1. 使用XMLEventReader,遍历文件
  2. 当遇到要改变属性的元素时,使用XMLEvent#getLocation(),然后在其上调用getCharacterOffset(),这将return原文件中的位置,其中此事件已发出。
  3. 通过跟踪前一元素和当前元素的偏移量,您可以从原始文件的内容中提取只有一个元素的子字符串。
  4. 更新子字符串,将其与其前后的文本连接起来,这将为您提供更新后的 XML 作为字符串。由于提取的子字符串仅包含一个元素,您可以安全地假设所有属性都是唯一的,因此您可以根据需要添加、删除和更新它们,而不必担心不小心触及其他元素。
  5. 将更新的内容作为字符串写入文件。

缺点:您必须手动解析属性,但在大多数情况下这是微不足道的。


此外,我发现 Characters 事件存在问题:它们是在后续 <</ 已经消耗后报告的。例如,在 <foo>bar</foo> 中, bar 个字符将被报告为 bar</.

这在 StAX 的其他实现中可能有所不同,我使用的是 Java 库中的默认实现。我假设这种行为可以解释为 StAX 解析器从不倒退,并且当它有足够的信息来检测字符结束事件时,它已经消耗了下一个元素(开始或结束标记)的开头。


至于我最初尝试使用XMLEventWriter:

  • XML encoding header 中缺少的 encoding 可以通过显式构造新的 StartDocument 事件来添加。
  • 可以手动添加缺少的换行符,但我找不到任何标志来保留它们。它似乎与上面的问题有关:解析器报告这些元素的偏移量以及换行符(有时它们是前置的,有时是附加的)。
  • 属性的随机顺序只能用自定义解析器固定,如@vtd-xml-author
  • 所述