XMLStreamReader.getLocation() 返回意外的字符偏移量

问题描述 投票:0回答:1

给出以下代码:

import javax.xml.stream.XMLInputFactory;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

class Scratch {
    public static void main(String[] args) throws Exception {
        var document = "<foo>bar</foo>";
        try (var is = new ByteArrayInputStream(document.getBytes(StandardCharsets.UTF_8))) {
            var reader = XMLInputFactory.newInstance().createXMLStreamReader(is);
            System.out.println(reader.getLocation());
        }
    }
}

我期望输出是

Line number = 1
Column number = 1
System Id = null
Public Id = null
Location Uri= null
CharacterOffset = 0

但它是

Line number = 1
Column number = 1
System Id = null
Public Id = null
Location Uri= null
CharacterOffset = 4

我很好奇 CharacterOffset = 4 这对我来说毫无意义。

有人可以解释一下为什么是4吗?

编辑:无论根元素如何,它始终为 4(

<foo
<foobar
都相同)

(此外:我需要有关 xml 的元素/标签位置的可靠信息,以便稍后根据该信息执行一些 DataBuffer 切片)

运行使用

  • 祖鲁 JDK 17
  • com.sun.org.apache.xerces 实施
java xml stax xerces
1个回答
0
投票

总结:

我正在使用 Adoptium 的 Temurin Java 21。

将字符串内容转换为XMLStreamReader所示,使用

StringReader
。这不会出现您所看到的偏移不准确的问题。


有关您所看到的结果的更多详细信息...

1)显式声明编码

在声明您的

XMLStreamReader
时,如果您使用
ByteArrayInputStream
,您应该明确提供您想要使用的编码。

所以,而不是这个:

XMLStreamReader reader = XMLInputFactory
        .newDefaultFactory()
        .createXMLStreamReader(is);

您可以使用这个:

XMLStreamReader reader = XMLInputFactory
        .newDefaultFactory()
        .createXMLStreamReader(is, "UTF-8");

(在这里,我用显式类

var
替换了你的
javax.xml.stream.XMLStreamReader
。)

您可以在此处查看此方法的 JavaDoc:

XMLInputFactory::createXMLStreamReader

该方法要求您使用字符串。我希望该课程早于

StandardCharsets
- 或者不使用这种首选方法。

您可能认为在定义字节数组输入流时已经声明了编码:

new ByteArrayInputStream(document.getBytes(StandardCharsets.UTF_8))

确实有 - 但

XMLInputFactory
并没有利用这一点。需要明确告知,如上所示。


2。自动检测编码

在幕后,Xerces 使用以下类:

com.sun.org.apache.xerces.internal.impl.XMLEntityManager

此类使用您定义的任何显式编码来构建适当的字节数组处理程序(它知道字节是如何编码的)。

如果您没有传递显式编码(就像问题中的代码没有传递的那样),那么

XMLEntityManager
会尝试从字节流的开头自动检测编码。它首先检查是否已使用 BOM。

您可以在源代码中看到这里

// perform auto-detect of encoding if necessary
if (encoding == null) {
    // read first four bytes and determine encoding
    final byte[] b4 = new byte[4];
    int count = 0;
    for (; count<4; count++ ) {
        b4[count] = (byte)rewindableStream.readAndBuffer();
    }
    .... // rest of code not shown here

这会消耗 XML 数据流的 前 4 个字节

如果没有BOM,则流被重置:

stream.reset();

但是,上面显示的

readAndBuffer()
方法在该
for
循环中被调用 4 次 - 并且该方法会递增偏移变量:
fOffset++;
- 您可以在此处的源代码中看到

因此,虽然流被重置,但偏移状态并未重置。

这就是你的结果的来源:

CharacterOffset = 4


3.如果您提供显式字符集“UTF-8”怎么办?

在这种情况下,代码将使用此信息

此处

在这种情况下,代码再次检查 BOM,但为此使用 3 字节数组(对于 3 字节 UTF-8 BOM):

final int[] b3 = new int[3];
因此,在这种情况下,我的代码版本会产生此输出,其基本原因与上面提到的相同:

CharacterOffset = 3
这次偏移量是3,而不是4。同上,流被重置;但偏移跟踪器未重置。


4。如果你使用StringReader

呢?

在这种情况下,

org.xml.sax.InputSource

类将使用以下内容来管理您的输入源:

public InputSource (Reader characterStream) { setCharacterStream(characterStream); }
事实上,您可以使用任何 

Reader

 子类 - 包括 
StringReader

在这些情况下,字节检查逻辑(如上所述)

被绕过。因此偏移量保持准确。

这是一个带有缓冲读取器和字符数组读取器的示例:

var is = new BufferedReader(new CharArrayReader(document.toCharArray()));
这给出:

CharacterOffset = 0


我不知道这是否会上升到错误的程度,或者只是 Xerces 工作方式的一个功能 - 并且是

设计 来工作的。

© www.soinside.com 2019 - 2024. All rights reserved.