Как заставить синтаксический анализатор SAX использовать DTD, если он не указан во входном файле?

Как заставить синтаксический анализатор SAX (в частности, Xerces in Java) использовать DTD при анализе документа без наличия любого doctype во входном документе? Возможно ли это?

Вот несколько подробностей моего сценария:

У нас есть куча XML-документов, которые соответствуют тому же DTD, которые генерируются несколькими разными системами (ни один из которых я не могу изменить). Некоторые из этих систем добавляют doctype к их выходным документам, другие - нет. Некоторые используют именованные объекты символов, некоторые - нет. Некоторые используют именованные объекты символов без объявления doctype. Я знаю, что не кошерный, но это то, с чем мне нужно работать.

Я работаю над системой, которая должна разбирать эти файлы на Java. В настоящее время он обрабатывает вышеуказанные случаи, сначала прочитав в документе XML как поток, пытаясь определить, имеет ли он определенный тип doctype, и добавляет объявление doctype, если оно еще не присутствует. Проблема в том, что этот код неисправен, и я бы хотел заменить его чем-то более чистым.

Файлы большие, поэтому Я не могу использовать DOM-решение. Я также пытаюсь получить символьные сущности, поэтому он не помогает использовать XML-схему.

Если у вас есть решение, не могли бы вы разместить его прямо, а не ссылку на него? Это не делает переполнение стека очень хорошим, если в будущем есть правильное решение с мертвой ссылкой.

Ответ 1

Я думаю, что это не разумный способ установить DOCTYPE, если в документе нет ни одного. Возможное решение - писать поддельные, как вы уже это делали. Если вы используете SAX, вы можете использовать эту поддельную InputStream и поддельную реализацию DefaultHandler. (будет работать только для одноканальной кодировки latin1)

Я знаю, что это решение также уродливо, но он работает только с большими потоками данных.

Вот какой код.

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
}