Why does this SAXParseException get thrown?

8.1k views Asked by At

I am somewhat at a loss with this problem. I am writing a short section of code that should validate an XML file against the corresponding XSD Schema. To test, I passed it a valid XML file (validated by whatever Netbeans uses). To my chagrin I received the following error message:

org.xml.sax.SAXParseException; cvc-elt.1: Cannot find the declaration of element 'map'.

This is strange given that Netbeans found the declaration of element 'map' just fine. I know that relative file paths can sometimes be a problem, so I substituted an absolute path. Result: same problem.

As a result I can only assume that the SAXParser doesn't use the xsi:schemaLocation attribute to determine the Schema file.

Does anyone know why I am expriencing this behaviour?

Here is an SSCCE:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;


public class ValidatorTest {

    public static void main(String[] args) {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document doc = null;
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            doc = db.parse(new FileInputStream(new File(args[0])));
        } catch (Throwable ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        SchemaFactory sf = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
        Schema schema = null;
        try {
            schema = sf.newSchema(new StreamSource(new File(args[1])));
        } catch (SAXException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
        Source source = new DOMSource(doc);
        Validator validator = schema.newValidator();
        try {
            validator.validate(source);
            System.out.println("SUCESS!");
        } catch (SAXException ex) {
            ex.printStackTrace();
            System.out.println("FAIL!");
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }
}

My XML:

<?xml version="1.0" encoding="UTF-8"?>
<map id="testMap" name="Test Map" xmlns="zorkCloneMapTypes"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="zorkCloneMapTypes /home/max/NetBeanzWorkspace/ZorkClone/src/main/resources/maps/betterMaps.xsd">
    <room id="testingGrounds" name="Testing Grounds" posX="0" posY="1">
        <type>basic</type>
        <description>A dusty arena, enclosed in a circular, wooden fence.  Clearly designed for practicing combat.  There are red blood spatters in the sand.</description>
        <passageNorth>true</passageNorth>
        <passageEast>false</passageEast>
        <passageSouth>false</passageSouth>
        <passageWest>false</passageWest>
        <enemies>
            <enemy>
                <name>Training Master</name>
                <type>Basic</type>
                <description>A Training Master. He will not let you pass until you defeat him in combat.</description>
                <level>1</level>
            </enemy>
        </enemies>
        <containers>
            <container locked="false">
                <name>Equipment locker.</name>
                <description>Equipment locker.</description>
                <level>5</level>
            </container>
            <container locked="false">
                <name>Equipment locker.</name>
                <description>Equipment locker.</description>
                <level>5</level>
            </container>
        </containers>
    </room>
    <transferRoom id="start" name="Starting Map" posX="0" posY="0">
        <type>transfer</type>
        <description>The starting room for the game.</description>
        <transferID>testMap.testingGrounds</transferID>
        <passageNorth>false</passageNorth>
        <passageEast>false</passageEast>
        <passageSouth>true</passageSouth>
        <passageWest>false</passageWest>
        <enemies/>
        <containers/>
    </transferRoom>
</map>

And my XSD:

    <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="zorkCloneMapTypes"
           targetNamespace="zorkCloneMapTypes"
           elementFormDefault="qualified">
  <xs:element name="map">
    <xs:complexType>
      <xs:sequence>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="room"/>
    <xs:element minOccurs="1" maxOccurs="unbounded" ref="transferRoom"/>
      </xs:sequence>
      <xs:attribute name="id" type="xs:ID" use="required"/>
      <xs:attribute name="name" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="transferRoom">
    <xs:complexType>
      <xs:sequence>
    <xs:element ref="type"/>
    <xs:element ref="description"/>
    <xs:element ref="transferID"/>
    <xs:element ref="passageNorth"/>
    <xs:element ref="passageEast"/>
    <xs:element ref="passageSouth"/>
    <xs:element ref="passageWest"/>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="enemies"/>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="containers"/>
      </xs:sequence>
      <xs:attribute name="name" use="required" type="xs:string"/>
      <xs:attribute name="id" use="required" type="xs:ID"/>
      <xs:attribute name="posX" use="required" type="xs:string"/>
      <xs:attribute name="posY" use="required" type="xs:string"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="room">
    <xs:complexType>
      <xs:sequence>
    <xs:element ref="type"/>
    <xs:element ref="description"/>
    <xs:element ref="passageNorth"/>
    <xs:element ref="passageEast"/>
    <xs:element ref="passageSouth"/>
    <xs:element ref="passageWest"/>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="enemies"/>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="containers"/>
      </xs:sequence>
      <xs:attribute name="name" use="required" type="xs:string"/>
      <xs:attribute name="id" use="required" type="xs:ID"/>
      <xs:attribute name="posX" use="required" type="xs:string"/>
      <xs:attribute name="posY" use="required" type="xs:string"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="containers">
    <xs:complexType>
      <xs:sequence>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="container"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="enemies">
    <xs:complexType>
      <xs:sequence>
    <xs:element minOccurs="0" maxOccurs="unbounded" ref="enemy"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="container">
    <xs:complexType>
      <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="description"/>
    <xs:element ref="level"/>
      </xs:sequence>
      <xs:attribute name="locked" default="false" type="xs:boolean"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="enemy">
    <xs:complexType>
      <xs:sequence>
    <xs:element ref="name"/>
    <xs:element ref="type"/>
    <xs:element ref="description"/>
    <xs:element ref="level"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="name" type="xs:string"/>
  <xs:element name="type" type="xs:string"/>
  <xs:element name="description" type="xs:string"/>
  <xs:element name="transferID" type="xs:string"/>
  <xs:element name="passageNorth" type="xs:boolean"/>
  <xs:element name="passageEast" type="xs:boolean"/>
  <xs:element name="passageSouth" type="xs:boolean"/>
  <xs:element name="passageWest" type="xs:boolean"/>
  <xs:element name="level" type="xs:int"/>
</xs:schema>
2

There are 2 answers

3
MystyxMac On

Maybe because your schema doesn't validate?

The fault in your schema is in the xs:all element.

From W3Schools:

The all element specifies that the child elements can appear in any order and that each child element can occur zero or one time.

But in your schema you defined some elements inside the xs:all as maxoccurs="unbounded" which is not allowed. E.G.:

<xs:all>
    <xs:element minOccurs="1" maxOccurs="unbounded" ref="transferRoom"/>
    <xs:element maxOccurs="unbounded" ref="room"/>
</xs:all>

Here the element "tranferRoom" can be more than 1. Try to delete the maxoccurs or replace the xs:all.

EDIT:

I did some tests and it worked when i removed all namespace declarations from xsd and xml (xmlns, targetnamespace, etc.). So i guess that there is something wrong with the namespace.

0
hhsh On

I think you were missing the key point.To correct,make sure the factory aware the namespace.

package xml;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

//import com.sun.org.apache.xerces.internal.parsers.DOMParser;
//import com.sun.org.apache.xerces.internal.parsers.DOMParser;

public class SchemaTest {
    public static void main(String args[]) {
         
         String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
         String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
        ErrorHandler errorHandler = new SimpleSaxErrorHandler();
        DelegatingEntityResolver entityResolver = new DelegatingEntityResolver();
        
        try {
            String s = "D:/workspace-202006/xml/ns/map.xml" ;
            String s2 ="D:/workspace-202006/xml/ns/fileCopyDemo-binary.xml";
            File file = new File(s);//
//          InputSource inputSource = file. new InputSource("");

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            factory.setValidating(true);
            factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            
            DocumentBuilder docBuilder = factory.newDocumentBuilder();
            docBuilder.setErrorHandler(errorHandler);
            docBuilder.setEntityResolver(entityResolver);
            docBuilder.parse(file);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class SimpleSaxErrorHandler implements ErrorHandler {

    @Override
    public void warning(org.xml.sax.SAXParseException exception) throws org.xml.sax.SAXException {
        exception.printStackTrace();
    }

    @Override
    public void error(org.xml.sax.SAXParseException exception) throws org.xml.sax.SAXException {
        exception.printStackTrace();
    }

    @Override
    public void fatalError(org.xml.sax.SAXParseException exception) throws org.xml.sax.SAXException {
        // TODO Auto-generated method stub
        
    }
}

class DelegatingEntityResolver implements EntityResolver {

    /** Suffix for DTD files. */
    public static final String DTD_SUFFIX = ".dtd";

    /** Suffix for schema definition files. */
    public static final String XSD_SUFFIX = ".xsd";


    private final EntityResolver dtdResolver;

    private final EntityResolver schemaResolver;


    /**
     * Create a new DelegatingEntityResolver that delegates to
     * the given {@link EntityResolver EntityResolvers}.
     * @param dtdResolver the EntityResolver to resolve DTDs with
     * @param schemaResolver the EntityResolver to resolve XML schemas with
     */
    public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {
        this.dtdResolver = dtdResolver;
        this.schemaResolver = schemaResolver;
    }
    public DelegatingEntityResolver() {
        this.dtdResolver = null;
        this.schemaResolver = null;
    }


    @Override
    public InputSource resolveEntity(  String publicId, String systemId)
            throws SAXException, IOException {

        if (systemId != null) {
            if (systemId.endsWith(DTD_SUFFIX)) {
                return this.dtdResolver.resolveEntity(publicId, systemId);
            }
            else if (systemId.endsWith(XSD_SUFFIX)) {
                return this.schemaResolver.resolveEntity(publicId, systemId);
            }
        }

        // Fall back to the parser's default behavior.
        return null;
    }


    @Override
    public String toString() {
        return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver +
                " and " + DTD_SUFFIX + " to " + this.dtdResolver;
    }

}