11

入力ドキュメントに doctype がなくてドキュメントを解析するときに、SAX パーサー (具体的には Java の Xerces) に DTD を使用させるにはどうすればよいですか? これは可能ですか?

私のシナリオの詳細は次のとおりです。

複数の異なるシステムによって生成された同じ DTD に準拠する XML ドキュメントが多数あります (どれも変更できません)。これらのシステムには、出力ドキュメントに Doctype を追加するものと、追加しないものがあります。名前付き文字エンティティを使用するものもあれば、使用しないものもあります。doctype を宣言せずに名前付き文字エンティティを使用するものもあります。 私はそれがコーシャではないことを知っていますが、それは私が取り組まなければならないものです.

これらのファイルを Java で解析する必要があるシステムに取り組んでいます。現在、XML ドキュメントを最初にストリームとして読み込み、Doctype が定義されているかどうかを検出し、Doctype 宣言がまだ存在しない場合は追加することで、上記のケースを処理しています。問題は、このコードにバグがあることです。よりクリーンなものに置き換えたいと考えています。

ファイルが大きいため、DOM ベースのソリューションを使用できません。また、文字エンティティを解決しようとしているので、XML スキーマを使用しても役に立ちません。

解決策がある場合は、リンクではなく直接投稿していただけませんか? 将来、リンク切れの正しい解決策がある場合、スタック オーバーフローはあまり効果がありません。

4

1 に答える 1

2

ドキュメントに DOCTYPE がない場合、DOCTYPE を設定するのは正気ではないと思います。可能な解決策は、すでに行っているように、偽物を書くことです。SAX を使用している場合は、この偽の InputStream および偽の DefaultHandler 実装を使用できます。(latin1 1 バイト エンコーディングでのみ機能します)

このソリューションも醜いことは知っていますが、ビッグデータストリームでうまく機能するのは1つだけです.

ここにいくつかのコードがあります。

private enum State {readXmlDec, readXmlDecEnd, writeFakeDoctipe,  writeEnd};

private class MyInputStream extends InputStream{

    private final InputStream is;
    private StringBuilder sb = new StringBuilder();
    private int pos = 0;
    private String doctype = "<!DOCTYPE register SYSTEM \"fake.dtd\">";
    private State state = State.readXmlDec;

    private MyInputStream(InputStream source) {
        is = source;
    }
    @Override
    public int read() throws IOException {
        int bit;

        switch (state){ 
            case readXmlDec:
                bit = is.read();
                sb.append(Character.toChars(bit));
                if(sb.toString().equals("<?xml")){
                    state = State.readXmlDecEnd;
                }
                break;
            case readXmlDecEnd:
                bit = is.read();
                if(Character.toChars(bit)[0] == '>'){
                    state = State.writeFakeDoctipe;
                }
                break;
            case writeFakeDoctipe:
                bit =  doctype.charAt(pos++);
                if(doctype.length() == pos){
                    state = State.writeEnd;
                }
                break;
            default:
                bit = is.read();
                break;
        }
        return bit;
    }

    @Override
    public void close() throws IOException {
        super.close();
        is.close();
    }
}

private static class MyHandler extends DefaultHandler {

    @Override
    public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException {
        System.out.println("resolve "+ systemId);
        // get real dtd
        InputStream is = ClassLoader.class.getResourceAsStream("/register.dtd");
        return new InputSource(is);
    }

 ... // rest of code
}
于 2012-12-27T15:42:53.280 に答える