Java XML validation does not work when schema comes from classpath

When you pass a File to StreamSource, the InputStream is set to the contents of the file, but also the systemId is set to be the URL of the File. This allows relative URIs in your schema to be resolved. If your schema has any relative URLs, this is definitely your problem. To make those relative URLs resolvable when reading the schema from the classpath, you need to implement an EntityResolver. If you don't use relative URIs there might still be other more subtle impacts of the systemId being null. I would recommend using the constructor

StreamSource(InputStream inputStream, String systemId)

Try setting systemId to: null, the File containing the schema, some other file, a File that doesn't exist. That might give you a hint of what Validator is doing with the systemId.


I found that I did not need to implement an EntityResolver to make relative URLs resolvable from the classpath.

It was sufficient to set the system id to the URI of the classpath resource.

The following is a worked example that uses Spring to build a list of StreamSources from .xsd files on the classpath.

Setup validation sources

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
Resource[] theResources = patternResolver.getResources("classpath:schemas/**/*.xsd");
List<Source> sources = new ArrayList<>();
for (Resource resource: theResources) {
    StreamSource dtd = new StreamSource(resource.getInputStream());
    dtd.setSystemId(resource.getURI().toString());
    sources.add(dtd);

The patternResolver is given a pattern of classpath:schemas/**/*.xsd which allows it to recursively find all .xsd files in the schemas directory on the classpath.

The .xsd files can import other .xsd files using relative paths. For example a .xsd file could include an import like this:

<xsd:import namespace="urn:www.example.com/common" schemaLocation="./common.xsd">

This line:

dtd.setSystemId(resource.getURI().toString());

is the key to having the relative paths in the .xsd files resolved by the schema validator.

Perform validation

The StreamSource array (sources) built above can now be used to set the schema sources for XML validation:

import org.xmlunit.builder.Input;
import org.xmlunit.validation.Languages;
import org.xmlunit.validation.Validator;
import javax.xml.transform.Source;

Validator v = Validator.forLanguage(Languages.W3C_XML_SCHEMA_NS_URI);
v.setSchemaSources(sources.toArray(new Source[sources.size()]));
Source input = Input.fromByteArray(xmlBytes).build();
v.validateInstance(input);

The validateInstance method call validates the XML represented by the xmlBytes array.