8

我正在使用 Xerces 来解析我的 XML 文档。问题是 XML 转义字符(如方法 characters()的非转义字符)。我需要在characters()方法中按原样获取转义字符。

谢谢。

UPD:试图覆盖resolveEntity()DefaultHandler的后代中的方法。从调试中可以看出,它被设置为 XML 阅读器的实体解析器,但未调用来自重写方法的代码。

4

4 回答 4

7

我认为您的解决方案还不错:几行代码就可以完全按照您的意愿行事。问题是startEntityendEntity方法不是由ContentHandler接口提供的,所以你必须编写一个LexicalHandler与你的ContentHandler. 通常,使用 anXMLFilter更优雅,但你必须使用实体,所以你仍然应该写一个LexicalHandler. 在这里查看有关 SAX 过滤器使用的介绍。

我想向您展示一种与您的非常相似的方法,它允许您将过滤操作(&例如包装 & to)与输出操作(或其他操作)分开。我已经写了我自己XMLFilter的基于XMLFilterImpl它也实现了LexicalHandler接口。此过滤器仅包含与实体转义/取消转义相关的代码。

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 {
    }
}

这是我的主要内容,它根据过滤器代码接收实体DefaultHandlerContentHandler

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我认为你没有更优雅的方式。

您还应该考虑切换到StaxParserXMLInputFactory.IS_REPLACING_ENTITY_REFERENCE:设置为 false可以很容易地做您想做的事情。如果你喜欢这个解决方案,你应该看看这里

于 2011-04-02T17:55:25.543 回答
5

如果您提供 LexicalHandler 作为 SAX 解析器的回调,它将使用 startEntity() 和 endEntity() 回调通知您每个实体引用的开始和结束。

(请注意,当正确的术语是“实体引用”时,http: //download.oracle.com/javase/1.5.0/docs/api/org/xml/sax/ext/LexicalHandler.html上的 JavaDoc 谈到了“实体” ”)。

另请注意,没有办法让 SAX 解析器告诉您有关数字字符引用的信息,例如&#x1234;. 应用程序应该以与原始角色完全相同的方式对待这些,所以你真的不应该对它们感兴趣。

于 2011-03-29T16:09:38.390 回答
1

临时解决方案:

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);
}

但仍然需要优雅的解决方案。

于 2011-03-30T14:26:29.553 回答
1

还有一个可能:类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);

你可以在这里下载 jar 。

于 2011-04-05T11:46:18.517 回答