6

我有一个包含数千个标签的 XML 文件来读取它们的文本内容,如下面的屏幕截图所示:

要读取的 XML 文件

我正在尝试使用以下代码读取所有“单词”标签的文本内容:

String filePath = "...";
File xmlFile = new File( filePath );

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document domObject = db.parse( xmlFile );
domObject.getDocumentElement().normalize();
NodeList categoryNodes = domObject.getElementsByTagName( "category" );   // Get all the <category> nodes.

for (int s = 0; s < categoryNodes.getLength(); s++) {    //Loop on the <category> nodes.
    String categoryName = categoryNodes.item(s).getAttributes().getNamedItem( "name" ).getNodeValue(); 

    if( selectedCategoryName.equals( categoryName ) ) {  //get its words.
        NodeList wordsNodes = categoryNodes.item(s).getChildNodes();

        for( int i = 0; i < wordsNodes.getLength(); i++ ) {
            if( wordsNodes.item( i ).getNodeType() != Node.ELEMENT_NODE ) continue;
            String word = wordsNodes.item( i ).getTextContent();
            categoryWordsList.add( word );  // Some words are read wrong !!
        }

        break;
    }
}

但是由于某种原因,许多单词被错误地阅读,例如:

"AMK6780KBU" is read as "9826</word"

"ASSI.ABR30326" is read as "rd>ASSI.AEP26"

"ASSI.25066" is read as "SI.4268</6"

这可能是因为文件大小很大。如果我只是从 XML 文件中添加一些空行或删除一些空行,其他单词会比上面提到的读错,这是一件奇怪的事情!

您可以从这里下载 XML 文件。

4

2 回答 2

3

解决方案

见下文 :-)

我在这个过程中尝试了什么

更改 XML 版本1.1 -> 1.0为我解决了这个问题。我正在使用Java 1.6.0_33(正如@orique 在评论中指出的那样)。

在我的测试中,在一定数量的节点之后肯定存在损坏问题。我把它缩小到某个地方ASSI.MTK69609。删除所有内容,包括该行修复了先前单词的损坏。

只需将声明更改为:

<?xml version="1.0">

我看到使用整个原始源 XML 的零损坏。

同样,如果您将版本保留在1.1但从源中删除空白节点,则结果如预期,例如:

    <word>ASSI.MTK68490</word>
    <word>ASSI.MTK6862617</word>
<word>ASSI.MTK693115</word>
<word>ASSI.MTK69609</word>

导致所需的输出和

    <word>ASSI.MTK68490</word>
    <word>ASSI.MTK6862617</word>
    <word>ASSI.MTK693115</word>
    <word>ASSI.MTK69609</word>

已损坏。

删除一些行尾“节点”也纠正了问题,例如

    <word>ASSI.MTK693115</word><word>ASSI.MTK69609</word>

所以这一切都指向一个错误,但在哪里......?最终它点击了!施尔塞斯

Java 1.6(可能还有 1.7)附带的 Xerces 版本是旧的、旧的、旧的和有缺陷的(例如#6760982)。事实上,我可以通过简单地添加以下内容来破坏我的测试类:

Document domObject = db.parse( xmlFile );
domObject.normalizeDocument(); // <-- causes following Exception

Exception in thread "main" java.lang.NullPointerException
    at com.sun.org.apache.xerces.internal.util.XML11Char.isXML11ValidNCName(XML11Char.java:340)

XML 1.1 修复了许多缺陷,因此我预感下载了最新版本Xerces2 Java 2.11.0

只需使用最新版本运行即可获得预期的未损坏输出。

java -classpath .;xercesImpl.jar;xml-apis.jar Foo > foo.txt
于 2013-03-21T15:24:11.480 回答
1

我们注意到这getTextContent()在某些 Windows 实现中存在错误。

我们的解决方法是做这样的事情

            // getTextContent is buggy on some Java Windows Implementations
            if ( n.getNodeType(  ) == Node.ELEMENT_NODE ) {

                results [ i ] = (String) xPathFunction.evaluate( "./text()", n, XPathConstants.STRING );
            } else {  //Node.TEXT_NODE

                results [ i ] = n.getNodeValue(  );
            }

xPathFunction是一个javax.xml.xpath.XPath。昂贵,但工作可靠。

实际上,在您的情况下,我会直接使用 XPath 并调用类似的东西,

NodeList l = (NodeList) xPathFunction.evaluate( "/categories/category/word/text()", domObject, XPathConstants.NODESET )

编辑

打败我!在 OSX、Java 1.6.0_43 上,我得到了相同的行为。如果对 Java 中的 DOM 模型有任何疑问......错误的值似乎可靠地出现在特定的时间间隔,这看起来像是一些字节缓冲区溢出。我从来没有遇到过OOM错误。

这是我尝试失败的方法:

  • word.getFirstChild().getNodeValue();而不是word.getTextContent();-> 行为没有变化
  • 使用 anInputSource作为输入DocumentBuilder而不是使用 aFile
  • 运行XPath("/categories/category[@name='Category1']/word/text()") 而不是遍历节点并手动遍历它们的子节点
  • 使用 Saxon 作为 XPath 引擎运行相同的测试
  • 检查 XML 文件中的“奇怪”字符

我相信DocumentBuilder是罪魁祸首。这是一个记忆猪。

您的下一个最佳机会是使用 SAX 解析器或任何其他流解析器。由于您的数据模型很小且非常简单,因此实现应该很容易。为了进一步简化实现,您可以尝试XMLDog。我们使用稍加修改的版本成功解析千兆字节大小的 XML 文件。

如果您发现问题,请更新此帖子。

于 2013-03-20T14:31:56.067 回答