Как я могу заставить парсер SAX использовать DTD, если он не указан во входном файле? - PullRequest
11 голосов
/ 10 марта 2010

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

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

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

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

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

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

1 Ответ

2 голосов
/ 27 декабря 2012

Я думаю, что это не нормальный способ установить 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
}
...