Using an entity resolver

Putting the SAX EntityResolver interface to work


Content series:

This content is part # of # in the series: Tip

Stay tuned for additional content in this series.

This content is part of the series:Tip

Stay tuned for additional content in this series.

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 &lt; 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 &copyright;) 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 Related topics 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:

package org.xml.sax;
public interface EntityResolver {
    public InputSource resolveEntity(String publicID, String systemID)
        throws SAXException;

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:

<!ENTITY copyright SYSTEM "http://www.ibm.com/developerWorks/copyright.xml">

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.

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;

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 instead of whatever resource is located at the supplied system ID. 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.

// Get an XML Reader - this code not detailed here
XMLReader reader = XMLReaderFactory.createXMLReader();
reader.setEntityResolver(new CopyrightResolver());
reader.parse(new InputSource("article.xml"));

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!

Downloadable resources

Related topics


Sign in or register to add and subscribe to comments.

ArticleTitle=Tip: Using an entity resolver