Using XML catalogs

When Saxon needs to access a resource with a URI, it invokes a URI resolver. The default URI resolver in Saxon can be configured with XML catalogs (defined by OASIS). Catalogs provide a way to avoid hard-coding the locations of XML documents and other resources in your application. Instead, the application refers to the resource using a conventional system identifier (URI) or public identifier, and a local catalog is used to map the system and public identifiers to an actual location.

When using Saxon from the command line, it is possible to specify which catalogs to use with the option -catalog:files. Here files is the catalog file to be searched, or a list of filenames separated by semicolons. This catalog will be used to locate DTDs and external entities required by the XML parser, XSLT stylesheet modules requested using xsl:import and xsl:include, documents requested using the document() and doc() functions, and also schema documents, however they are referenced.

Similarly, when using the API, catalog files can be nominated:

The catalog is used for all kinds of external resources, including XML documents, JSON documents, query modules, unparsed text files, and schema modules (but not collations or collections). The kind of resource required is generally identified by the nature parameter, following the principles of the RDDL specification. The values used are listed in constants in class ResourceRequest on Java (or ResourceRequest on C#).

Catalog-based resolution is performed using a third-party library:

See the documentation for the relevant library for options on how the catalog resolver can be configured, for example using system properties or a properties file. Remember that such settings are typically global (they may affect more than one Saxon Configuration).

Settings local to a configuration can be made by getting the ResourceResolver from the Configuration, casting it to class ConfigurableResourceResolver, and calling its setFeature() method.

Note: Saxon configures the resolver with an explicitly empty list of catalog files and does not use catalog files configured with properties or property files. This is a compromise for backwards compatibility because, historically, Saxon only used a catalog resolver if an explicit -catalog option was specified on the command line. If Saxon configured the resolver to use the global configuration, then it would always attempt to load ./catalog.xml as a catalog file and that could change the behavior of existing applications. You must use the -catalog option or settings local to the Saxon Configuration to specify the catalogs used by Saxon.

Here is an example of a very simple catalog file. The publicId and systemId attributes give the public or system identifier as used in the source document; the uri attribute gives the location (in this case a relative location) where the actual resource will be found.

<?xml version="1.0"?> <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> <group prefer="public" xml:base="file:///usr/share/xml/" > <public publicId="-//OASIS//DTD DocBook XML V4.5//EN" uri="docbook45/docbookx.dtd"/> <system systemId="http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" uri="docbook45/docbookx.dtd"/> </group> </catalog>

There are many tutorials for XML catalogs available on the web, including some that have information specific to Saxon, but take care that it is relevant to the current release. Significant changes have been made in Saxon 11.1.

Historically, the entity resolver contract on Java was that the resolver should return null if it failed to resolve the request. In terms of the catalog resolver, if you requested a resource and the resource wasn't in the catalog, the resolver would return null and the parser (or other processor) would perhaps try to resolve the request some other way.

As web servers began to transition from "http" URIs to "https" URIs, a new problem arose. To ease the transition to https, most web servers will return a "redirect" response if you attempt to use the now deprecated "http" URI. But some parsers don't understand how to follow redirects.

If you request "http://example.com/doctype.dtd" and that isn't in the catalog, the catalog resolver returns null, the parser attempts to retrieve the resource at that URI, the web server returns a redirect response, and the parser falls over.

To mitigate this problem, there's an XML Resolver feature, ALWAYS_RESOLVE, that's enabled by default. When this feature is enabled, rather than returning null if the resource isn't in the catalog, the resolver will make the request to the server, follow redirects if they're encountered, and return the resource.

This, unfortunately, is not without its drawbacks. Because the resolver always returns a resource if one exists, the caller will not perform whatever fallback behavior would have occurred if null had been returned. In practice, this is rarely a problem, but if your application relies on fallback behavior, you must either assure that resources not in the catalog do not exist, or that they're the correct kind of document for the request, or disable the ALWAYS_RESOLVE feature.

There's no answer that's best under all circumstances.

This problem doesn't arise in SaxonCS because the entity resolver contract in .NET is different. The resolver is always expected to return the resource.