エラーを含んだXMLをルーズにパースする

各種ブログのRSSのようなWeb上のXMLリソースをdom4jやJDOMなどで読み込むと、パースに失敗するケースがとても多いです。というのも、こういうXMLは基本的に、validであることをあまり期待できないからです(エスケープ漏れがあったり、"<!--"で始まったコメントの直後に"-"が来たりする[追記: これはinvalidな例じゃなく非well-formedな例でした])。ひどいときはwell-formedですらないこともあります。

こういう問題がある場合、HTMLであれば、MayaaS2JSFでも採用されているNekoHTMLというライブラリを使って、エラーを出さずにルーズにパースできます。このNekoHTMLを、HTMLではなくXMLに適用する方法を調べたので、メモしておきます。

パーサを以下のような構成にすると、XMLの解析に適した状態になります。

  • NekoHTML側ではなくXerces側のDOMParserを使う(NekoHTMLのDOMParserを使うと要素名と属性名が強制的に大文字になってしまう)
  • DOMParserで"elems"と"attrs"プロパティを"default"にする
  • DOMParserで"balance-tags"機能をオフにする
  • DOMParserで"filters"プロパティを空にする

コード例は以下です。

import java.io.IOException;
import java.io.InputStream;

import org.apache.xerces.parsers.DOMParser;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import org.cyberneko.html.HTMLConfiguration;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * @author Kaisei Hamamoto
 */

public class LooseXMLParser {
    public Document parse(InputStream in, String encoding) throws SAXException,
            IOException {
        HTMLConfiguration config = new HTMLConfiguration();
        DOMParser parser = new DOMParser(config);

        parser.setProperty("http://cyberneko.org/html/properties/names/elems",
                "default");
        parser.setProperty("http://cyberneko.org/html/properties/names/attrs",
                "default");

        parser.setFeature("http://cyberneko.org/html/features/balance-tags",
                false);
        parser.setProperty("http://cyberneko.org/html/properties/filters",
                new XMLDocumentFilter[0]);

        InputSource source = new InputSource(in);
        source.setEncoding(encoding);

        parser.parse(source);
        return parser.getDocument();
    }
}