级别: 初级 Brett McLaughlin (brett@newInstance.com), Enhydra 策略顾问, Lutris Technologies
2001 年 6 月 01 日 本提示讨论了如何使用外部实体引用使 XML 文档中包含外部内容及如何使用 SAX EntityResolver 接口。代码样本说明了该接口及其使用的文档,并演示了如何在分析器中注册实体解析器。
大多数 XML 和 HTML 开发人员都熟悉实体引用,一种常见的有点奇怪的 XML 结构,以 & 符号开始,以分号 (;) 结束。或许,实体引用最常见的用法就是按字面意思解释 XML 中的非法字符,如
< 表示 XML 或 HTML 元素起始的左尖括号(< 也可作为 小于符号 )。
有些开发人员使用另一种实体引用方法使 XML
文档中包含来自其它源的一些资料, 这些资料通常是希望在多个 XML
文档中共享的一段内容,也可能是诸如 Flash 动画那样的不能转换成 XML
的项。
这种实体引用称为
外部实体引用,它节省了您可能花在一次次复制并粘贴那些内容的时间。例如,
您可能在 XML 文档中使用实体引用(如
©right; )对由其他人负责更新的另一个文档中的样板文件进行引用。
(可是,如果没有 DTD 或 XML
模式,外部实体引用就不能工作,因此也就根本不需要去尝试。)
分析实体引用及相关问题
在进行 XML 分析时,分析器使用 DTD 或 XML
模式中指定的位置
解析 XML 文档中的外部实体引用(在本提示中,
主要关注 DTD,因为 DTD
是当前产品应用程序中最常用的)。解析期间,分析器会找到被引用的内容并将它插入
XML 中。 这表示:当您在操纵已分析过的文档(用
Java、C、Perl、PHP、Python
或其它任何语言)时,被引用的内容与其它内容会以相同方式显示。
只要各方面都正常工作,则不必考虑逐个处理每一段引用内容。但是,有些复杂情况会导致这一简单过程的中止。
例如,您可能需要一个活动的网络连接以使解析能够正确进行,因为有许多被引用的实体都引用了位于某处的一个远程
URL(例如,“清单 3”中示例代码
中的http://www.ibm.com/developerworks/copyright.xml)。解析(打开连接、下载内容、关闭连接等)也会减慢分析进程的速度。
您可能想知道是否存在一种方法在本地提供了被引用内容的高速缓存副本,或有另一种方法能绕过实体解析进程。
很高兴告诉大家,的确有这种方法。
解析外部实体引用的简单方法
如果您在使用“XML 的简单 API (SAX)”,那么您非常幸运!而且,由于
DOM 和 JDOM 都暗中使用 SAX, 所以这一简单解决方案对三种 API
都适用(请参阅
参考资料获得有关这三种 API
的背景知识)。SAX 定义了一个接口
org.xml.sax.EntityResolver ,它提供的功能就是您要的功能。这个接口只定义了一个方法,如“清单
1”中所示:
清单 1. SAX
EntityResolver 接口
package org.xml.sax;
public interface EntityResolver {
public InputSource resolveEntity(String publicID, String systemID)
throws SAXException;
}
|
这个接口中的唯一方法
resolveEntity()
提供了一种开始实体解析进程的方法。由于每个外部实体引用在指定如何解析内容的
DTD
中都有一个公共标识或一个系统标识,或同时拥有这两者,所以可以在这个方法中匹配它们并实现自己的行为。例如,认为“清单
2”中的 DTD 部分定义了
copyright 外部实体引用:
清单 2. DTD
中定义的 copyright 外部实体引用
<!ENTITY copyright SYSTEM "http://www.ibm.com/developerworks/copyright.xml"> |
这里,没有公共标识,系统标识是
http://www.ibm.com/developerworks/copyright.xml 。
所以,可以创建一个名为
CopyrightEntityResolver
的类,如“清单 3”中所示。
清单 3. 实现
EntityResolver
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() 方法。如果实体的系统标识匹配该方法中的
URL, 则返回本地的 XML 文档
(
localCopyright.xml );
而不是返回位于提供的系统标识处的任何资源。
使用这种方法,可以“缩短”处理进程,并向给定的公共标识或系统标识提供您自己的数据。当不匹配时,您会希望始终确保返回
null ,这样实体解析在非特殊情况下都可以正常进行。
以上就是对实体引用解析的介绍。您可以将实体解析器注册在分析器上,如“清单
4”中所示。
清单 4. 实现
EntityResolver
// 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 文档的灵活性。尽情享用吧!
参考资料
关于作者  | |  | Brett McLaughlin
(brett@newInstance.com) 在 Lutris Technologies 担当 Enhydra
策略顾问, 专攻分布式系统体系结构。他是
Java 和 XML
(O'Reilly) 的作者。 他还参与了诸如 Java Servlet、Enterprise
JavaBeans 技术、XML 和商家对商家应用程序等技术的研究。 他与 Jason
Hunter 一起建立了 JDOM 项目,该项目提供了一个简单的 API 从 Java
应用程序操纵 XML。他还是 Apache Cocoon 项目、EJBoss EJB
服务器的主要开发人员,并且是 Apache Turbine 项目的共同创始人。
|
对本文的评价
|