使用 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 文件中查找标签的位置(字符偏移)并进行子字符串替换。你可以这样做:
- 使用
XMLEventReader
,遍历文件
- 当遇到要改变属性的元素时,使用
XMLEvent#getLocation()
,然后在其上调用getCharacterOffset()
,这将return原文件中的位置,其中此事件已发出。
- 通过跟踪前一元素和当前元素的偏移量,您可以从原始文件的内容中提取只有一个元素的子字符串。
- 更新子字符串,将其与其前后的文本连接起来,这将为您提供更新后的 XML 作为字符串。由于提取的子字符串仅包含一个元素,您可以安全地假设所有属性都是唯一的,因此您可以根据需要添加、删除和更新它们,而不必担心不小心触及其他元素。
- 将更新的内容作为字符串写入文件。
缺点:您必须手动解析属性,但在大多数情况下这是微不足道的。
此外,我发现 Characters
事件存在问题:它们是在后续 <
或 </
已经消耗后报告的。例如,在 <foo>bar</foo>
中, bar
个字符将被报告为 bar</
.
这在 StAX 的其他实现中可能有所不同,我使用的是 Java 库中的默认实现。我假设这种行为可以解释为 StAX 解析器从不倒退,并且当它有足够的信息来检测字符结束事件时,它已经消耗了下一个元素(开始或结束标记)的开头。
至于我最初尝试使用XMLEventWriter
:
- XML
encoding
header 中缺少的 encoding
可以通过显式构造新的 StartDocument 事件来添加。
- 可以手动添加缺少的换行符,但我找不到任何标志来保留它们。它似乎与上面的问题有关:解析器报告这些元素的偏移量以及换行符(有时它们是前置的,有时是附加的)。
- 属性的随机顺序只能用自定义解析器固定,如@vtd-xml-author
所述
我正在尝试使用 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 文件中查找标签的位置(字符偏移)并进行子字符串替换。你可以这样做:
- 使用
XMLEventReader
,遍历文件 - 当遇到要改变属性的元素时,使用
XMLEvent#getLocation()
,然后在其上调用getCharacterOffset()
,这将return原文件中的位置,其中此事件已发出。 - 通过跟踪前一元素和当前元素的偏移量,您可以从原始文件的内容中提取只有一个元素的子字符串。
- 更新子字符串,将其与其前后的文本连接起来,这将为您提供更新后的 XML 作为字符串。由于提取的子字符串仅包含一个元素,您可以安全地假设所有属性都是唯一的,因此您可以根据需要添加、删除和更新它们,而不必担心不小心触及其他元素。
- 将更新的内容作为字符串写入文件。
缺点:您必须手动解析属性,但在大多数情况下这是微不足道的。
此外,我发现 Characters
事件存在问题:它们是在后续 <
或 </
已经消耗后报告的。例如,在 <foo>bar</foo>
中, bar
个字符将被报告为 bar</
.
这在 StAX 的其他实现中可能有所不同,我使用的是 Java 库中的默认实现。我假设这种行为可以解释为 StAX 解析器从不倒退,并且当它有足够的信息来检测字符结束事件时,它已经消耗了下一个元素(开始或结束标记)的开头。
至于我最初尝试使用XMLEventWriter
:
- XML
encoding
header 中缺少的encoding
可以通过显式构造新的 StartDocument 事件来添加。 - 可以手动添加缺少的换行符,但我找不到任何标志来保留它们。它似乎与上面的问题有关:解析器报告这些元素的偏移量以及换行符(有时它们是前置的,有时是附加的)。
- 属性的随机顺序只能用自定义解析器固定,如@vtd-xml-author 所述