Most XML and HTML developers are familiar with entity references, the odd little XML constructs you often see that begin with an ampersand (&) and end with a semicolon (;). Probably the most common use of entity references is to literalize characters that aren't legal in XML, such as <
to represent the opening angle bracket (<, also known as the less than symbol) that begins an XML or HTML element.
Some developers use another kind of entity reference as a way to include in an XML document some material from a different source -- often a piece of content that's intended to be shared across multiple XML documents, or perhaps an item such as a Flash animation sequence that cannot be converted to XML. That kind of entity reference, called an external entity reference, saves the time you might otherwise spend copying and pasting the content over and over again. For instance, you might use an entity reference (such as ©right;
) in an XML document to reference boilerplate sections in a different document that someone else is responsible for keeping up to date. (If you don't have a DTD or XML schema, though, external entity references won't work, so don't even try it.)
Parsing entity references, and the associated problems
When XML parsing occurs, the parser resolves the external entity reference in an XML document using the location specified in the DTD or XML schema (In this tip, I'm focusing on DTDs because today they are more commonly used in production applications). During resolution, the parser locates the referenced content and inserts it into the XML. This means that when you manipulate the parsed document (in Java, C, Perl, PHP, Python, or whatever other language you are using), the referenced content appears just as any other content would. As long as everything works properly, you don't have to worry about handling each piece of referenced content individually. Complications, however, may cause the simple process to break down.
You may, for example, need a live network connection in order for the resolution to work properly because so many referenced entities refer to a remote URL somewhere (for instance, http://www.ibm.com/developerWorks/copyright.xml in the example code in Listing 3). The resolution (opening up a connection, pulling down content, closing the connection, and so on) also may slow down the parsing process. You may begin to wonder if there's a way to provide cached, local copies of the referenced pieces of content or another way to circumvent the entity-resolution process. I'm happy to report that there is.
A simple way to resolve external entity references
As long as you're using the Simple API for XML (SAX), you're in luck! And since both DOM and JDOM use SAX under the hood, this simple solution works for all of three APIs (see for background on all three APIs). SAX defines an interface, org.xml.sax.EntityResolver
, that provides just the functionality you want. This interface defines only one method, as shown in Listing 1:
1 2 3 4 5 |
|
The sole method in this interface, resolveEntity()
, provides a means to step into the entity-resolution process. Because each external entity reference has either or both a public ID and a system ID in the DTD specifying how to resolve the content, you can match these up in this method and implement your own behavior. For example, consider the DTD fragment in Listing 2 that defines the copyright
external entity reference:
1 |
|
Here, there is no public ID, and the system ID is http://www.ibm.com/developerWorks/copyright.xml
. So, you could create a class called CopyrightEntityResolver
as shown in Listing 3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
In this simple implementation, the resolveEntity()
method will be invoked every time an entity is resolved. If the system ID for the entity matches the URL in that method, a local XML document (localCopyright.xml
) is returned; this is insteadof whatever resource is located at the supplied system ID. (在这个方法中,如果这个实体的system ID匹配了URL地址的值。将会加载本地的XML文档作为输入流byteStream返回,就不需要根据URL地址进行网络加载了,如果那个byteStream返回null,后面的程序还是会根据system ID指定的地址去加载实体引用文件。EntityResolver主要就是起这个作用的!!!)In this way, you can "short circuit" the process and supply your own data for a given public or system ID. You'll want to be sure to always return null
if no match occurs, so that entity resolution will occur normally in non-special cases.
That's about all there is to it. You can register your entity resolver on your parser as shown in Listing 4.
1 2 3 4 |
|
So there you have it. If you can obtain local copies of entity reference content, or if you need to substitute your own content for an entity reference, use the SAX EntityResolver
interface. This should help speed your applications and increase the flexibility of your XML documents. Enjoy!