Tre*_*kaz 7 java xml parsing stax
我们有一个系统,我们正在处理XML文件,其中文件本身太大而无法放入内存中.
作为处理的一部分,我们希望快速扫描以记录相关元素的偏移量,以便稍后,我们可以立即查找这些元素并解析我们想要的部分(因为文件的较小片段将适合内存) ,我们可以负担得起使用DOM或其他任何部分.)
显然,我们可以从头开始编写自己的XML解析器,但在创建另一个XML解析器之前,我想看看是否还有其他可用选项.
以下是我们已经了解的事项列表.
使用StAX应该可以工作,但不能.这是一个演示.我做了一个XML示例,其中有超过一个字节的字符,以证明一旦开始传递这些字符,返回的字节偏移量就不正确.请注意,即使API中的方法名为getCharacterOffset(),文档也会说如果传入字节流,它会返回字节偏移量 - 这就是此代码正在执行的操作.
@Test
public void testByteOffsetsFromStreamParser() throws Exception {
// byte counts are size required for UTF-8, I checked using Ishida's tool.
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<root>\n"
" <leaf>\u305A\u308C\u306A\u3044\u3067\u307B\u3057\u3044</leaf>\n" +
" <leaf>\u305A\u308C\u306A\u3044\u3067\u307B\u3057\u3044</leaf>\n" +
" <leaf>\u305A\u308C\u306A\u3044\u3067\u307B\u3057\u3044</leaf>\n" +
"</root>\n";
byte[] xmlBytes = xml.getBytes("UTF-8");
assertThat(xmlBytes.length, is(equalTo(171))); // = 171 from above
String implToTest = "com.sun.xml.internal.stream.XMLInputFactoryImpl";
//String implToTest = "com.ctc.wstx.stax.WstxInputFactory";
XMLInputFactory factory =
Class.forName(implToTest).asSubclass(XMLInputFactory.class).newInstance();
factory.setProperty("javax.xml.stream.isCoalescing", false);
factory.setProperty("javax.xml.stream.supportDTD", false);
XMLEventReader reader = factory.createXMLEventReader(
new ByteArrayInputStream(xmlBytes));
try {
XMLEvent event;
event = reader.nextTag(); // <root>
checkByteOffset(event, 39);
event = reader.nextTag(); // <leaf>
checkByteOffset(event, 47);
event = reader.nextEvent(); // (text)
checkByteOffset(event, 53);
event = reader.nextTag(); // </leaf>
checkByteOffset(event, 77);
event = reader.nextTag(); // <leaf>
checkByteOffset(event, 86);
event = reader.nextEvent(); // (text)
checkByteOffset(event, 92);
event = reader.nextTag(); // </leaf>
checkByteOffset(event, 116);
event = reader.nextTag(); // <leaf>
checkByteOffset(event, 125);
event = reader.nextEvent(); // (text)
checkByteOffset(event, 131);
event = reader.nextTag(); // </leaf>
checkByteOffset(event, 155);
event = reader.nextTag(); // </root>
checkByteOffset(event, 163);
} finally {
reader.close(); // no auto-close :(
}
}
private void checkByteOffset(XMLEvent event, int expectedOffset) {
System.out.println("Expected Offset: " + expectedOffset +
" - Actual Offset: " + event.getLocation().getCharacterOffset());
}
Run Code Online (Sandbox Code Playgroud)
您在Java 7中默认获得的工厂的结果:
Expected Offset: 39 - Actual Offset: 45
Expected Offset: 47 - Actual Offset: 53
Expected Offset: 53 - Actual Offset: 63
Expected Offset: 77 - Actual Offset: 68
Expected Offset: 86 - Actual Offset: 76
Expected Offset: 92 - Actual Offset: 86
Expected Offset: 116 - Actual Offset: 91
Expected Offset: 125 - Actual Offset: 99
Expected Offset: 131 - Actual Offset: 109
Expected Offset: 155 - Actual Offset: 114
Expected Offset: 163 - Actual Offset: 122
Run Code Online (Sandbox Code Playgroud)
Woodstox的结果,我们根据其他一些stackoverflow post建议尝试了.请注意,虽然它开始是正确的,但在几行之后,它甚至比默认解析器更不正确:
Expected Offset: 39 - Actual Offset: 39
Expected Offset: 47 - Actual Offset: 47
Expected Offset: 53 - Actual Offset: 53
Expected Offset: 77 - Actual Offset: 61
Expected Offset: 86 - Actual Offset: 70
Expected Offset: 92 - Actual Offset: 76
Expected Offset: 116 - Actual Offset: 84
Expected Offset: 125 - Actual Offset: 93
Expected Offset: 131 - Actual Offset: 99
Expected Offset: 155 - Actual Offset: 107
Expected Offset: 163 - Actual Offset: 115
Run Code Online (Sandbox Code Playgroud)我们知道一个名为VTD-XML的库,它几乎完全符合我们的要求,但它有两个问题.第一个问题是它将整个文件读入内存并且文件本身不适合.第二个问题是许可证是GPL并且与我们的其他东西不兼容.
可能的方法:
1)以字节流方式打开文件。
2) 围绕输入流/读取器 (a) 从 UTF-8 转换为 UTF-16,但 (b) 在此过程中跟踪哪些 Java 字符是基本 ASCII 范围以及哪些是 2 字节 UTF16。(我可以想出几种方法来将跟踪的内存需求保持在合理的范围内。)
3) 当您需要文件偏移量时,使用该跟踪表将 Java UTF-16 字符计数反向转换为字节计数。
想不出任何理由为什么它不起作用......