xml 解析实体
大多数XML和HTML开发人员都熟悉实体引用,您经常会看到奇怪的小XML构造,它们以与号(&)开头,以分号(;)结尾。 实体引用的最常见用法可能是将XML中不合法的字符(例如<
表示以XML或HTML元素开头的尖括号(<,也称为小于符号)。
一些开发人员使用另一种实体引用作为将来自不同来源的某些材料包括在XML文档中的方式-通常是打算在多个XML文档之间共享的一部分内容,或者可能是诸如Flash动画序列之类的项目无法转换为XML。 这种类型的实体引用(称为外部实体引用 )可以节省您一遍又一遍地复制和粘贴内容所花费的时间。 例如,您可以在XML文档中使用实体引用(例如©right;
)来引用其他人负责保持最新状态的另一个文档中的样板节。 (但是,如果您没有DTD或XML模式,则外部实体引用将不起作用,因此甚至不要尝试。)
解析实体引用以及相关的问题
发生XML解析时,解析器使用DTD或XML模式中指定的位置解析 XML文档中的外部实体引用(在本技巧中,我将重点放在DTD上,因为今天它们在生产应用程序中更常用)。 在解析过程中,解析器查找引用的内容并将其插入XML。 这意味着,当您操作解析的文档(使用Java,C,Perl,PHP,Python或您使用的任何其他语言)时,引用的内容将与其他任何内容一样显示。 只要一切正常,您就不必担心分别处理每个引用的内容。 但是,复杂性可能会导致简单过程崩溃。
例如,您可能需要实时网络连接才能使解析正常工作,因为如此多的引用实体都引用某个地方的远程URL(例如,http://www.ibm.com/developerWorks/copyright.xml)。清单3中的示例代码)。 分辨率(打开连接,拉下内容,关闭连接等)也可能会减慢解析过程。 您可能会开始怀疑,是否有一种方法可以提供所引用内容的缓存本地副本,或者是另一种规避实体解析过程的方法。 我很高兴地报告有。
解决外部实体引用的简单方法
只要您使用的是XML的简单API(SAX),就很幸运! 而且由于DOM和JDOM都在后台使用SAX,因此此简单的解决方案适用于所有三个API(请参阅参考资料 ,了解所有三个API的背景知识)。 SAX定义了一个接口org.xml.sax.EntityResolver
,该接口仅提供所需的功能。 该接口仅定义一种方法,如清单1所示:
package org.xml.sax;
public interface EntityResolver {
public InputSource resolveEntity(String publicID, String systemID)
throws SAXException;
}
此接口中唯一的方法resolveEntity()
提供了一种进入实体解析过程的方法。 由于每个外部实体引用在DTD中都有公共ID和系统ID中的一个或两个,用于指定如何解析内容,因此您可以在此方法中将它们进行匹配并实现自己的行为。 例如,考虑清单2中的DTD片段,该片段定义了copyright
外部实体引用:
<!ENTITY copyright SYSTEM "http://www.ibm.com/developerWorks/copyright.xml">
在这里,没有公共ID,系统ID是http://www.ibm.com/developerWorks/copyright.xml
。 因此,您可以创建一个名为CopyrightEntityResolver
的类,如清单3所示。
package com.ibm.developerWorks;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class CopyrightResolver implements EntityResolver {
public InputSource resolveEntity(String publicID, String systemID)
throws SAXException {
if (systemID.equals("http://www.ibm.com/developerWorks/copyright.xml")) {
// Return local copy of the copyright.xml file
return new InputSource("/usr/local/content/localCopyright.xml");
}
// If no match, returning null makes process continue normally
return null;
}
在此简单的实现中,每次解析实体时都会调用resolveEntity()
方法。 如果实体的系统ID与该方法中的URL相匹配,则返回本地XML文档( localCopyright.xml
); localCopyright.xml
,返回localCopyright.xml
。 而不是位于提供的系统ID处的任何资源。 这样,您可以“短路”该过程,并为给定的公共ID或系统ID提供您自己的数据。 如果没有匹配项,您将要确保始终返回null
,以便实体解析在非特殊情况下通常会发生。
这就是全部。 您可以在解析器上注册您的实体解析器,如清单4所示。
// Get an XML Reader - this code not detailed here
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setEntityResolver(new CopyrightResolver());
reader.parse(new InputSource("article.xml"));
所以你有它。 如果可以获取实体参考内容的本地副本,或者需要用自己的内容替换实体参考,请使用SAX EntityResolver
接口。 这将有助于加快应用程序的速度并增加XML文档的灵活性。 请享用!
翻译自: https://www.ibm.com/developerworks/xml/library/x-tipent/index.html
xml 解析实体