【问题标题】:SAX parser: Ignoring special charactersSAX 解析器:忽略特殊字符
【发布时间】:2011-03-29 15:42:20
【问题描述】:

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

谢谢。

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

【问题讨论】:

标签: java xml parsing sax saxparser


【解决方案1】:

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

我想向您展示一种与您的方法非常相似的方法,它允许您将过滤操作(例如包装 & 到 &)与输出操作(或其他操作)分开。我已经基于XMLFilterImpl 编写了我自己的XMLFilter,它也实现了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,我认为您没有更优雅的方式。

您还应该考虑切换到StaxParser:将XMLInputFactory.IS_REPLACING_ENTITY_REFERENCE 设置为false 很容易做您想做的事。如果你喜欢这个解决方案,你应该看看here

【讨论】:

  • 其实 startEntity 可能只是 "char[] c = { '&' }; characters(c, 0, 1);"。这稍微更有效,因为它不涉及创建一些临时字符串,并且得到相同的结果。
【解决方案2】:

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

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

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

【讨论】:

  • 谢谢,但我怎样才能拦截解析实体,而不仅仅是知道它们已被解析?
  • +1 表示“应用程序应该以与原始字符完全相同的方式对待这些”:我认为 OP 正在尝试做一些 XML 试图使之不可能的事情。
  • 有时您可能会关心原始序列化 XML 源中的确切字符偏移量,即使 XML 从业者认为这无关紧要 - 有时 XML 只是一个文件。在这种情况下,您确实需要关心数字实体和它们所代表的字符之间的区别(我认为内置 XML 实体 < " 和 & 可能存在类似问题)。 Woodstox 4(StaX XML 解析器)可以在解析时提供此信息,但我不相信 Xerces 可以。
【解决方案3】:

临时解决办法:

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

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

【讨论】:

  • 你实现了哪些接口?这有哪些超类?想到的三四个都不行,这个例子真的不完整。
【解决方案4】:

还有一个可能:org.apache.commons.lang.StringEscapeUtils类的escapeXml方法。

在您的 characters(char[] ch, int start, int length) 方法中尝试此代码:

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

您可以下载jar here

【讨论】:

  • 转义 xml 实体的好主意,但它不能与问题中要求的 &amp;nbsp; 一起正常工作。也许你可以使用StringEscapeUtils#escapeHtml,但你可能会有一些副作用。例如,如果 xml 包含字符串 My name is&amp;nbsp;javanna,那么您的输出应该是 My&amp;nbsp;name&amp;nbsp;is&amp;nbsp;javanna,因此您不能只保留原始的 &amp;nbsp;。 @Aleksander O:你的 xml 中有 ` ` 和 &amp;nbsp; 吗?你能接受这种副作用吗?
猜你喜欢
  • 2012-10-31
  • 1970-01-01
  • 1970-01-01
  • 2015-09-10
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 2011-04-08
  • 2022-12-11
相关资源
最近更新 更多