是否有一个很好的XML解析器用于光扫描XML文件以获取元素的字节偏移量?

Tre*_*kaz 7 java xml parsing stax

我们有一个系统,我们正在处理XML文件,其中文件本身太大而无法放入内存中.

作为处理的一部分,我们希望快速扫描以记录相关元素的偏移量,以便稍后,我们可以立即查找这些元素并解析我们想要的部分(因为文件的较小片段将适合内存) ,我们可以负担得起使用DOM或其​​他任何部分.)

显然,我们可以从头开始编写自己的XML解析器,但在创建另一个XML解析器之前,我想看看是否还有其他可用选项.

以下是我们已经了解的事项列表.

  1. 使用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)
  2. 我们知道一个名为VTD-XML的库,它几乎完全符合我们的要求,但它有两个问题.第一个问题是它将整个文件读入内存并且文件本身不适合.第二个问题是许可证是GPL并且与我们的其他东西不兼容.

kes*_*lam 1

可能的方法:

1)以字节流方式打开文件。

2) 围绕输入流/读取器 (a) 从 UTF-8 转换为 UTF-16,但 (b) 在此过程中跟踪哪些 Java 字符是基本 ASCII 范围以及哪些是 2 字节 UTF16。(我可以想出几种方法来将跟踪的内存需求保持在合理的范围内。)

3) 当您需要文件偏移量时,使用该跟踪表将 Java UTF-16 字符计数反向转换为字节计数。

想不出任何理由为什么它不起作用......