SAX parser: игнорирование специальных символов - PullRequest
8 голосов
/ 29 марта 2011

Я использую Xerces для разбора моего XML-документа.Проблема в том, что экранированные символы xml, такие как '', появляются в методе characters () как не экранированные.Мне нужно получить экранированные символы в методе characters () как есть.

Спасибо.

UPD: попытался переопределить метод resolEntity в потомке моего DefaultHandler.Из отладки можно увидеть, что он установлен как распознаватель сущностей для xml reader, но код из переопределенного метода не вызывается.

Ответы [ 4 ]

7 голосов
/ 02 апреля 2011

Я думаю, что ваше решение не так уж и плохо: несколько строк кода, чтобы сделать именно то, что вы хотите.Проблема в том, что методы startEntity и endEntity не предоставляются интерфейсом ContentHandler, поэтому вы должны написать LexicalHandler, который работает в сочетании с вашим ContentHandler.Обычно использование XMLFilter более элегантно, но вы должны работать с сущностью, поэтому вам все равно следует написать LexicalHandler.Взгляните здесь на введение в использование фильтров SAX.

Я хотел бы показать вам способ, очень похожий на ваш, который позволяет вам разделять операции фильтрации (обертывание& to & например) от операций вывода (или чего-то еще).Я написал свой собственный XMLFilter на основе XMLFilterImpl, который также реализует интерфейс LexicalHandler.Этот фильтр содержит только код, связанный с entites escape / unescape.

public class XMLFilterEntityImpl extends XMLFilterImpl implements
        LexicalHandler {

    private String currentEntity = null;

    public XMLFilterEntityImpl(XMLReader reader)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        super(reader);
        setProperty("http://xml.org/sax/properties/lexical-handler", this);
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        if (currentEntity == null) {
            super.characters(ch, start, length);
            return;
        }

        String entity = "&" + currentEntity + ";";
        super.characters(entity.toCharArray(), 0, entity.length());
        currentEntity = null;
    }

    @Override
    public void startEntity(String name) throws SAXException {
        currentEntity = name;
    }

    @Override
    public void endEntity(String name) throws SAXException {
    }

    @Override
    public void startDTD(String name, String publicId, String systemId)
            throws SAXException {
    }

    @Override
    public void endDTD() throws SAXException {
    }

    @Override
    public void startCDATA() throws SAXException {
    }

    @Override
    public void endCDATA() throws SAXException {
    }

    @Override
    public void comment(char[] ch, int start, int length) throws SAXException {
    }
}

И это мой основной метод, с DefaultHandler как ContentHandler, который получает сущность в соответствии с кодом фильтра:

public static void main(String[] args) throws ParserConfigurationException,
        SAXException, IOException {

    DefaultHandler defaultHandler = new DefaultHandler() {
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            //This method receives the entity as is
            System.out.println(new String(ch, start, length));
        }
    };

    XMLFilter xmlFilter = new XMLFilterEntityImpl(XMLReaderFactory.createXMLReader());
    xmlFilter.setContentHandler(defaultHandler);
    String xml = "<html><head><title>title</title></head><body>&amp;</body></html>";
    xmlFilter.parse(new InputSource(new StringReader(xml)));
}

И это мой вывод:

title
&amp;

Возможно, вам это не нравится, в любом случае, это альтернативное решение.

Извините,но с SaxParser я думаю, что у вас нет более элегантного способа.

Вы также должны рассмотреть переключение на StaxParser: очень легко делать то, что вы хотите с XMLInputFactory.IS_REPLACING_ENTITY_REFERENCE установлено в false.Если вам нравится это решение, вы должны взглянуть здесь .

5 голосов
/ 29 марта 2011

Если вы предоставляете LexicalHandler в качестве обратного вызова для синтаксического анализатора SAX, он будет информировать вас о начале и конце каждой ссылки на сущность, используя обратные вызовы startEntity () и endEntity ().

(Обратите внимание, что JavaDocв http://download.oracle.com/javase/1.5.0/docs/api/org/xml/sax/ext/LexicalHandler.html говорится о «сущностях», когда правильным термином является «ссылки на сущности»).

Обратите также внимание на то, что нет способа получить парсер SAX, чтобы сообщить вам о ссылках на числовые символы, таких как&#x1234;.Приложения должны обрабатывать их точно так же, как и исходный персонаж, так что вам действительно не нужно их интересовать.

1 голос
/ 05 апреля 2011

Существует еще один способ: escapeXml метод org.apache.commons.lang.StringEscapeUtils класса.

Попробуйте этот код в вашем characters(char[] ch, int start, int length) методе:

String data=new String(ch, start, length);
String escapedData=org.apache.commons.lang.StringEscapeUtils.escapeXml(data);

Вы можете скачать банку здесь .

1 голос
/ 30 марта 2011

Временное решение:

public void startEntity(String name) throws SAXException {
    inEntity = true;
    entityName = name;
}

public void characters(char[] ch, int start, int length) throws SAXException {
    String data;
    if (inEntity) {
        inEntity = false;
        data = "&" + entityName + ";";
    } else {
        data = new String(ch, start, length);
    }
    //TODO do something instead of System.out
    System.out.println(data);
}

Но все же нужно элегантное решение.

...