【问题标题】:Reading XML file returns wrong characters读取 XML 文件返回错误字符
【发布时间】:2013-04-24 19:52:04
【问题描述】:

我有一个包含数千个标签的 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 文件中添加一些空行或删除一些空行,其他单词会比上面提到的读错,这很奇怪!

您可以从here 下载 XML 文件。

【问题讨论】:

  • header说文件是UTF-8的,真的是这样吗?
  • 是的,有问题吗?
  • 我用 Firefox 打开了 XML,它抱怨 XML 声明格式不正确。它抱怨1.1 版本。也许您的解析器对 1.1 XML 文件也有问题?
  • andyb .. 我已经用新的 pastebin 链接更新了帖子,这里是 XML 链接:pastebin.com/embed_js.php?i=7rafYLDm
  • 适用于 jdk 1.7 并在 windows 7 上捆绑 xerces。您是否尝试过使用最新的 Xerces-J 2.11.0

标签: java xml readfile


【解决方案1】:

解决方案

见下文:-)

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

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

在我的测试中,在一定数量的节点之后肯定会出现损坏问题。我把它缩小到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>

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

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)

已经有many defects fixed for XML 1.1了,所以预感我下载了最新版Xerces2 Java 2.11.0

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

java -classpath .;xercesImpl.jar;xml-apis.jar Foo > foo.txt

【讨论】:

  • 太棒了。感谢您为此付出的努力。我检查了为什么我们没有有这个问题:我们在认可的库中有一个最新版本的 xercesImpl.jar...
  • andyb .. 当我再次尝试将版本更改为 1.0 时,我发现它正在工作。我为此向“orique”道歉。当我第一次测试这个时,我似乎做错了什么。根据您的解释,我希望使用 XML 1.1。我已尝试像您一样删除所有文件空间,但文件中仍然出现错误字符。我通过简单地在java代码读取的单词中查找“
  • andyb .. 我已将新的 Xerces2 Java 2.11.0 附加到我的应用程序中,这导致正确读取 XML1.1 文件 :) .. 不幸的是,这为我的应用程序增加了 1.5 兆字节,但我看到它是可用的最佳解决方案。谢谢安迪 :)
【解决方案2】:

我们注意到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(); -> 行为没有变化
  • 使用InputSource 作为DocumentBuilder 的输入,而不是使用File
  • 运行 XPath ("/categories/category[@name='Category1']/word/text()"),而不是遍历节点并手动遍历其子节点
  • 使用 Saxon 作为 XPath 引擎运行相同的测试
  • 检查 XML 文件中的“奇怪”字符

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

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

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

【讨论】:

  • 谢谢..你能给我更多关于这个解决方案的细节吗? ..我真的不明白!
  • 使用xPathFactory = XPathFactory.newInstance(); XPath xPathFunction = xPathFactory.newXPath( ); 获取XPath 然后运行上面的evaluate;或者,只需在您的代码中替换 wordsNodes.item( i ).getTextContent();by wordsNodes.item( i ).getNodeValue();;如果你 wordNodes 都是 Node.TEXT_NODE 类型,它可能会起作用
  • 不,getNodeValue() 总是为我返回 null。我所有的节点都是 Node.ELEMENT_NODE 类型。
  • 然后您需要在此 ELEMENT_NODE 中获取文本节点并调用 getNodeValue 或如上所述使用 xPath 调用 (String) xPathFunction.evaluate( "./text()", n, XPathConstants.STRING );。老实说,您应该选择 XPath 解决方案,您将在一行调用中替换嵌套循环和 ifs,然后是一个简单的循环,使您的代码看起来更具可读性。
  • 我用过:String word = wordsNodes.item( i ).getFirstChild().getNodeValue(); .. 结果完全一样!
猜你喜欢
  • 1970-01-01
  • 2012-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
相关资源
最近更新 更多