给出以下代码:
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 切片)
运行使用
总结:
我正在使用 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
。)
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
设计 来工作的。