2

I'm attempting to validate some XML against a relative XSD file in java

input file temp.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<tagname:b
        xmlns:tagname="http://my_namespace.org/v1"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://my_namespace.org/v1 ./local_xsd_file.xsd">
</tagname>

java source:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new RaiseOnErrorHandler());
builder.parse(new InputSource(new FileInputStream(filename)));

public static class RaiseOnErrorHandler implements ErrorHandler {
  public void warning(SAXParseException e) throws SAXException {
    throw new RuntimeException(e);
  }
  public void error(SAXParseException e) throws SAXException {
    throw new RuntimeException(e);
  }
  public void fatalError(SAXParseException e) throws SAXException {
    throw new RuntimeException(e);
  }
}

The local file local_xsd_file.xsd exists and appears to be not being read.

error message

java.lang.RuntimeException: org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 85; schema_reference.4: Failed to read schema document './local_xsd_file.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
    at org.XX$RaiseOnErrorHandler.warning(DigitalFileWriterTest.java:1114)
    at org.apache.xerces.util.ErrorHandlerWrapper.warning(Unknown Source)
    at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
    at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
    at org.apache.xerces.impl.xs.traversers.XSDHandler.reportSchemaWarning(Unknown Source)
    at org.apache.xerces.impl.xs.traversers.XSDHandler.getSchemaDocument(Unknown Source)
    at org.apache.xerces.impl.xs.traversers.XSDHandler.parseSchema(Unknown Source)
    at org.apache.xerces.impl.xs.XMLSchemaLoader.loadSchema(Unknown Source)
    at org.apache.xerces.impl.xs.XMLSchemaValidator.findSchemaGrammar(Unknown Source)
    at org.apache.xerces.impl.xs.XMLSchemaValidator.handleStartElement(Unknown Source)
    at org.apache.xerces.impl.xs.XMLSchemaValidator.startElement(Unknown Source)
    at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
    at org.apache.xerces.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    ... 50 more

I have also tried using file://local_xsd_file.xsd and file://./local_xsd_file.xsd (which should be possible) as the schemaLocation, all with the same results. If I use an "absolute path" like file:///full/path/to/local_xsd_file.xsd then it works. If I use a URL for the schema's location it reads them off that server and works fine.

Changing my current working directory to be one where a local_xsd_file.xsd exists will make it work, so apparently this works but only relative to your current working directory?

Is it possible to use a relative path with validation in java? It feels like an implementation detail perhaps?

rogerdpack
  • 62,887
  • 36
  • 269
  • 388

2 Answers2

2

When you build a document like this:

DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new RaiseOnErrorHandler());
builder.parse(new InputSource(new FileInputStream(filename)));

the base URI of the document is not known (because the builder only sees an InputStream, and has no way of finding out that this is reading a particular file). Try using one of the parse() methods from which a base URI can be established, e.g. parse(file.toString()) or saxParser.parse(file, (DefaultHandler) null);.

Without a base URI, there is no sensible way to resolve a relative URI. The system might use the current directory (which seems to be happening here) or it might report an error.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
0

For followers, if you are validating with an external schema file, it also seems possible, just avoid changing it to string first, ex:

public static void verifyMatchesXsd(File xmlFileToTest, File schemaFile) throws IOException, SAXException {
    Source xmlFile = new StreamSource(xmlFileToTest);
    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = schemaFactory.newSchema(schemaFile);
    javax.xml.validation.Validator validator = schema.newValidator();
    validator.validate(xmlFile);
}

or version without having to specify where external XSD is (it uses the internal):

  private void assertMatchesAnyXsdsMentioned(File xmlFileLocation) throws SAXException, IOException {
    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = schemaFactory.newSchema();
    Validator validator = schema.newValidator();
    Source xmlSource = new StreamSource(xmlFileLocation);
    validator.validate(xmlSource);
  }

You maybe could also specify your own resource resolver and have it know which subdir you want, too...

Sax parser version:

  static void assertMatchesInternalXsd(File inputFile) throws Exception {
    SAXParserFactory spf = SAXParserFactory.newInstance();
    spf.setValidating(true);
    spf.setNamespaceAware(true);
    SAXParser saxParser = spf.newSAXParser();
    saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
    XMLReader reader = saxParser.getXMLReader();
    reader.setErrorHandler(new RaiseOnErrorHandler());
    saxParser.parse(inputFile, (DefaultHandler) null);
  }
rogerdpack
  • 62,887
  • 36
  • 269
  • 388