Tuesday, July 12, 2005

Xml schemas and Namespaces?

With the amount of headaches I'm having doing a simple task like validating an Xml doc against a schema, I'm back to wondering if I shouldn't just throw this programming lark aside..

....

In the meantime heres the root cause of my most recent heartache....

The solution is ...

String xml ="<envelope><header2>Hi</header2></envelope>";
String xsd = "http://localhost:8080/xml/test.xsd";
SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser", true);
builder.setFeature("http://apache.org/xml/features/validation/schema", true);
builder.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",xsd);
builder.setValidation(true);
builder.build(new InputSource(new StringReader(xml)));


Simple isn't it

What could have been the problem?

The problem is that innocuous line builder.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",xsd);

To cut a long story short Xerces has 2 modes for validating external schemas (i.e. not the schemas referenced in the Xml doc itself). 1/ Namespace enabled 2/ No Namespace.

Simple huh?

Well it is once you know that simple fact. I on the other hand didn't . (Mind you I do think it should be possbile to validate an arbitary Xml doc without necessarily knowing if its namespace enabled or not. But thats for another day)

So in summary people

If you want to avoid cryptic exceptions like

org.jdom.input.JDOMParseException: Error on line 1: cvc-elt.1: Cannot find the declaration of element 'envelope'.
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:466)
at XmlTest.main(XmlTest.java:41)
Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'envelope'.
.....


NB. This is the same exception you get if Xerces cannot locate your schema file, or if you don't configure the paramerters correctly. Something more intuitive would be more helpful.

do the following

1/ If your Xml doc uses namespaces

String xml ="<envelope><header2>Hi</header2></envelope>";
// Namespae of xsd1
String ns1 = "http://www.w3.org/2001/12/soap-envelope"
String xsd1 = "http://localhost:8080/xml/test.xsd";
//Namespace of xsd2
String ns2 = "http://kevinj.develop.com/weblog/weblog.xsd";
String xsd2 = "http://localhost:8080/xml/test2.xsd";

SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser", true);
builder.setFeature("http://apache.org/xml/features/validation/schema", true);
builder.setProperty(
"http://apache.org/xml/properties/schema/external-schemaLocation", ns1+" "+xsd1+" + ns2+" "+xsd2);
builder.setValidation(true);
builder.build(new InputSource(new StringReader(xml)));


You must set the Property http://apache.org/xml/properties/schema/external-schemaLocation with a space seperated String list of the schema(s) to validate against.
The list takes the following form namespace uri namespace uri .... Its composed of pairs of values, the namespace that the schema will validate and the actual location of the schema file. (It can be any uri http://, file:// etc)

This section is taken from the JDOM FAQ


Schema locations are given by setting the property "http://apache.org/xml/properties/schema/external-schemaLocation" to a list of whitespace separated name-value pairs. The 'name' is the namespace the schema is associated with, the 'value' is the location of the schema for that namespace. For example:

builder.setProperty(
"http://apache.org/xml/properties/schema/external-schemaLocation", "http://www.w3.org/2001/12/soap-envelope soap-envelope.xsd" + " " + "http://kevinj.develop.com/weblog/weblog.xsd weblog.xsd");

The above example shows how to validate against multiple schemas -- against the SOAP 1.2 schema where the namespace is http://www.w3.org/2001/12/soap-envelope and the and against a schema for namespace http://kevinj.develop.com/weblog/weblog.xsd. The files describing these schemas are in soap-envelope.xsd and weblog.xsd respectively. You can add as many of these name value pairs as necessary. The values themselves are URLs. The name value pairs follow the meaning given in the Schema recommendation (http://www.w3.org/TR/xmlschema-1/#schema-loc ).




2/ If your Xml doc does not use namespaces

String xml ="<envelope><header2>Hi</header2></envelope>";
String xsd = "http://localhost:8080/xml/test.xsd";
SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser", true);
builder.setFeature("http://apache.org/xml/features/validation/schema",true);
builder.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",xsd);
builder.build(new InputSource(new StringReader(xml)));


Here we set the propertyhttp://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation (spot the difference) with simply the schema uri. (Not a List as there is only the default namespace) to validate against, and only a single items since there is no namespaces, (i.e. not a pair like before)


It may look simple but that wee little problem had me stumped for longer than I'd care to mention.

Heres some links on the stuff

The Jdom faq Jdom uses xerces so this applies to xerces as well.
xerces bugzilla entry. There was a bug with this prior to version 2.2.0

One aside. By default xerces will not throw exceptions for validation failures, the parser must be configured to do that.

Jdom will configure the parser to do this by default. (for a lazy programmer like me this simply means that I use jdom to wrap my calls to xerces, rather than actually looking up how to configure xerces)

14 comments:

Anonymous said...

Hi K,

You saved my life ... well at least my weekend ;-) Thanks for these tips!

Chris

Anonymous said...

Thanks a bunch, I was about to express my rage in unhealthy (to others) ways before I found this post. Now it's time to get drunk!

Anonymous said...

Hi,
I had followed the steps as mentioned but for me it is throwing JDOMException, mentioning that "the property is not recognized for SAX driver".
Please may i now which version of jdom jar file you have used..

thanks in advance,
Meena.

k said...

I've used lots of different jdom versions. I would urge you to check the JDOM faq at http://www.jdom.org/docs/faq.html#a0360

It says all versions above 0.8 Beta should work.

Also check your configuration. For me there's always a possiblity I've done something daft. Are you using xerces under jdom? Is that configured correctly (ie. can you parse a simple xml doc without validation)?

Once you know that is working then start to track down why the validation isn't working. Are your schema urls correct? (Can you use a browser to access them?)

Anonymous said...

Hi K,
Thank you for your immediate reply..
I had checked in jdom faq, they mentioned that xerces 2 and above will support schema validation so i used jdom 1.0 xerces 2.7.1 previously, it didn't work, but now i'm using jdom 1.0 and xerces 2.8.0. It started working.
Thanks a lot for helping:-)
Meena.

Anonymous said...

Hi russ,
Have you specified the schema location and schema name in the root element. Also check the namespace. I think you may commited mistake in that.Have you checked the version of the jar file.
thanks and regards,
Meena.

Anonymous said...

Hi, I had also this kind of problem when validating XML with XSD and with help of your article I have solved it. But I used standalone XSD with no namespace.
Now I have XSD which is importing another XSD which contains some common data types. In Netbeans IDE I debugged it - so schemas are valid. But I got the very known exception:

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

This is that root element in XML:
<SecDb xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2001/XMLSchema AppRepository2.xsd http://url/ns/something dataTypes.xsd">

That XMLSchema namesepace is targetNamespace in AppRepository2.xsd and http://url/ns/something is targetNamespace in dataTypes.xsd.

Can someone help?

Anonymous said...

I just wanted to thank you for your explanations. I was struggling with this, and you helped me resolve my issue.
Thanks.

Anonymous said...

Ou yeah!!! It's work!!!)))
Thank you very much.

Anonymous said...

Hello,

I receive the same error, but I can not solve it... I don't use a builder where I can set the Property.

The code:

FileOutputStream fileOutputStream=new FileOutputStream("C: /xml/catalogueSAX.xml");

OutputFormat outputFormat=new OutputFormat("XML","ISO-8859-1",false);
outputFormat.setIndent(0);
XMLSerializer xmlSerializer=new XMLSerializer(fileOutputStream,outputFormat);
ContentHandler contentHandler= xmlSerializer.asContentHandler();


String language=XMLConstants.W3C_XML_SCHEMA_NS_URI;
SchemaFactory schemaFactory=SchemaFactory.newInstance(language);
final String OUR_SCHEMA="C:/xmlfiles/catalogueBig.xsd";
Source schemaFile=new StreamSource(new FileInputStream(OUR_SCHEMA));
Schema schema=schemaFactory.newSchema(schemaFile);
ValidatorHandler validatorForSaxHandler=schema.newValidatorHandler();
validatorForSaxHandler.setErrorHandler(new ErrorHandlerImpl());
validatorForSaxHandler.setContentHandler(contentHandler);

Catalogue catalogue=getCatalogue(10,10);

validatorForSaxHandler.startDocument();
validatorForSaxHandler.startElement("", "", "catalogue", new AttributesImpl());
....
validatorForSaxHandler.endElement("", "", "catalogue");
validatorForSaxHandler.endDocument();




Does somebody know how to solve this error:
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'catalogue'.


Thanks in advance!

Anonymous said...

It is some bug and normally it it solved in jdk 1.5
see http://mail-archives.apache.org/mod_mbox/xerces-j-dev/200605.mbox/%3c3262346.1146854608199.JavaMail.jira@brutus%3e

But I tried to use jdk 1.5 and I still receive this error!

Is there somebody who knows what to do?

Thanks!

Alejandro De León said...

Thanks! it did save me a long time... hopefully this issues will fade away as the language becomes more elegant....

Kind Regards

Alejandro

Anonymous said...

Thank you very much. Thx to your advice I could fix my problem very fast.

Anonymous said...

Many thanks! I didn't manage to understand this "couples" (namespace,xsd-file). What a headhache!