How to get error details from JAXB Validator? - java

I have some classes with JAXB annotations, I have created some instances and I need to validate them against my XSD files. I should be able to get the details of what is wrong when the objects are invalid.
So far I haven't had luck, I know about this class ValidationEventHandler but apperantly I can use it with the Unmarshaller class, the problem is that I have to validate the objects not the raw XML.
I have this code:
MyClass myObject = new MyClass();
JAXBContext jaxbContext = JAXBContext.newInstance("x.y.z");
JAXBSource jaxbSource = new JAXBSource(jaxbContext, myObject);
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source schemaFile = new StreamSource(getClass().getClassLoader()
.getResourceAsStream("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(jaxbSource);
This code will work, it will validate the object and throw an exception with the message, something like this:
cvc-pattern-valid: Value '12345678901' is not facet-valid with respect
to pattern '\d{10}' for type 'id'.]
The problem is that I need specific details, with a string like that I would have to parse all the messages.

You can set an instance of ErrorHandler on the Validator to catch individual errors:
Validator validator = schema.newValidator();
validator.setErrorHandler(new MyErrorHandler());
validator.validate(source);
MyErrorHandler
Below is a sample implementation of the ErrorHandler interface. If you don't rethrow the exception the validation will continue.
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class MyErrorHandler implements ErrorHandler {
public void warning(SAXParseException exception) throws SAXException {
System.out.println("\nWARNING");
exception.printStackTrace();
}
public void error(SAXParseException exception) throws SAXException {
System.out.println("\nERROR");
exception.printStackTrace();
}
public void fatalError(SAXParseException exception) throws SAXException {
System.out.println("\nFATAL ERROR");
exception.printStackTrace();
}
}
For More Information
http://blog.bdoughan.com/2010/11/validate-jaxb-object-model-with-xml.html

I. If you validate a complex object hierarchy, you can create the Marshaller yourself and set its listener:
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setListener(yourListener);
JAXBSource source = new JAXBSource(marshaller, object);
This listener will get notified with instances of your objects as it walks the hierarchy.
II. Add an ErrorHandler from the other answer. At least with Wildfly 15 the messages look like:
cvc-maxInclusive-valid: Value '360.953674' is not facet-valid with respect to maxInclusive '180.0' for type '#AnonType_longitudeGeographicalPosition'.'
cvc-type.3.1.3: The value '360.953674' of element 'longitude' is not valid.'
So you can parse out the element name, which is the guilty terminal field name.
III. Combine I and II with some introspection and you can reconstruct a full Java Beans style path to the erroneous field if necessary.

Related

Java Validate XML Schema with XSDs that import each other [duplicate]

I'm trying to validate an XML file against a number of different schemas (apologies for the contrived example):
a.xsd
b.xsd
c.xsd
c.xsd in particular imports b.xsd and b.xsd imports a.xsd, using:
<xs:include schemaLocation="b.xsd"/>
I'm trying to do this via Xerces in the following manner:
XMLSchemaFactory xmlSchemaFactory = new XMLSchemaFactory();
Schema schema = xmlSchemaFactory.newSchema(new StreamSource[] { new StreamSource(this.getClass().getResourceAsStream("a.xsd"), "a.xsd"),
new StreamSource(this.getClass().getResourceAsStream("b.xsd"), "b.xsd"),
new StreamSource(this.getClass().getResourceAsStream("c.xsd"), "c.xsd")});
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(xmlContent)));
but this is failing to import all three of the schemas correctly resulting in cannot resolve the name 'blah' to a(n) 'group' component.
I've validated this successfully using Python, but having real problems with Java 6.0 and Xerces 2.8.1. Can anybody suggest what's going wrong here, or an easier approach to validate my XML documents?
So just in case anybody else runs into the same issue here, I needed to load a parent schema (and implicit child schemas) from a unit test - as a resource - to validate an XML String. I used the Xerces XMLSchemFactory to do this along with the Java 6 validator.
In order to load the child schema's correctly via an include I had to write a custom resource resolver. Code can be found here:
https://code.google.com/p/xmlsanity/source/browse/src/com/arc90/xmlsanity/validation/ResourceResolver.java
To use the resolver specify it on the schema factory:
xmlSchemaFactory.setResourceResolver(new ResourceResolver());
and it will use it to resolve your resources via the classpath (in my case from src/main/resources). Any comments are welcome on this...
http://www.kdgregory.com/index.php?page=xml.parsing
section 'Multiple schemas for a single document'
My solution based on that document:
URL xsdUrlA = this.getClass().getResource("a.xsd");
URL xsdUrlB = this.getClass().getResource("b.xsd");
URL xsdUrlC = this.getClass().getResource("c.xsd");
SchemaFactory schemaFactory = schemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
//---
String W3C_XSD_TOP_ELEMENT =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
+ "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\">\n"
+ "<xs:include schemaLocation=\"" +xsdUrlA.getPath() +"\"/>\n"
+ "<xs:include schemaLocation=\"" +xsdUrlB.getPath() +"\"/>\n"
+ "<xs:include schemaLocation=\"" +xsdUrlC.getPath() +"\"/>\n"
+"</xs:schema>";
Schema schema = schemaFactory.newSchema(new StreamSource(new StringReader(W3C_XSD_TOP_ELEMENT), "xsdTop"));
The schema stuff in Xerces is (a) very, very pedantic, and (b) gives utterly useless error messages when it doesn't like what it finds. It's a frustrating combination.
The schema stuff in python may be a lot more forgiving, and was letting small errors in the schema go past unreported.
Now if, as you say, c.xsd includes b.xsd, and b.xsd includes a.xsd, then there's no need to load all three into the schema factory. Not only is it unnecessary, it will likely confuse Xerces and result in errors, so this may be your problem. Just pass c.xsd to the factory, and let it resolve b.xsd and a.xsd itself, which it should do relative to c.xsd.
From the xerces documentation :
http://xerces.apache.org/xerces2-j/faq-xs.html
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
...
StreamSource[] schemaDocuments = /* created by your application */;
Source instanceDocument = /* created by your application */;
SchemaFactory sf = SchemaFactory.newInstance(
"http://www.w3.org/XML/XMLSchema/v1.1");
Schema s = sf.newSchema(schemaDocuments);
Validator v = s.newValidator();
v.validate(instanceDocument);
I faced the same problem and after investigating found this solution. It works for me.
Enum to setup the different XSDs:
public enum XsdFile {
// #formatter:off
A("a.xsd"),
B("b.xsd"),
C("c.xsd");
// #formatter:on
private final String value;
private XsdFile(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
Method to validate:
public static void validateXmlAgainstManyXsds() {
final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
String xmlFile;
xmlFile = "example.xml";
// Use of Enum class in order to get the different XSDs
Source[] sources = new Source[XsdFile.class.getEnumConstants().length];
for (XsdFile xsdFile : XsdFile.class.getEnumConstants()) {
sources[xsdFile.ordinal()] = new StreamSource(xsdFile.getValue());
}
try {
final Schema schema = schemaFactory.newSchema(sources);
final Validator validator = schema.newValidator();
System.out.println("Validating " + xmlFile + " against XSDs " + Arrays.toString(sources));
validator.validate(new StreamSource(new File(xmlFile)));
} catch (Exception exception) {
System.out.println("ERROR: Unable to validate " + xmlFile + " against XSDs " + Arrays.toString(sources)
+ " - " + exception);
}
System.out.println("Validation process completed.");
}
I ended up using this:
import org.apache.xerces.parsers.SAXParser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
.
.
.
try {
SAXParser parser = new SAXParser();
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setFeature("http://apache.org/xml/features/validation/schema", true);
parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "http://your_url_schema_location");
Validator handler = new Validator();
parser.setErrorHandler(handler);
parser.parse("file:///" + "/home/user/myfile.xml");
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException ex) {
e.printStackTrace();
}
class Validator extends DefaultHandler {
public boolean validationError = false;
public SAXParseException saxParseException = null;
public void error(SAXParseException exception)
throws SAXException {
validationError = true;
saxParseException = exception;
}
public void fatalError(SAXParseException exception)
throws SAXException {
validationError = true;
saxParseException = exception;
}
public void warning(SAXParseException exception)
throws SAXException {
}
}
Remember to change:
1) The parameter "http://your_url_schema_location" for you xsd file location.
2) The string "/home/user/myfile.xml" for the one pointing to your xml file.
I didn't have to set the variable: -Djavax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema=org.apache.xerces.jaxp.validation.XMLSchemaFactory
Just in case, anybody still come here to find the solution for validating xml or object against multiple XSDs, I am mentioning it here
//Using **URL** is the most important here. With URL, the relative paths are resolved for include, import inside the xsd file. Just get the parent level xsd here (not all included xsds).
URL xsdUrl = getClass().getClassLoader().getResource("my/parent/schema.xsd");
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsdUrl);
JAXBContext jaxbContext = JAXBContext.newInstance(MyClass.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
/* If you need to validate object against xsd, uncomment this
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<MyClass> wrappedObject = objectFactory.createMyClassObject(myClassObject);
marshaller.marshal(wrappedShipmentMessage, new DefaultHandler());
*/
unmarshaller.unmarshal(getClass().getClassLoader().getResource("your/xml/file.xml"));
If all XSDs belong to the same namespace then create a new XSD and import other XSDs into it. Then in java create schema with the new XSD.
Schema schema = xmlSchemaFactory.newSchema(
new StreamSource(this.getClass().getResourceAsStream("/path/to/all_in_one.xsd"));
all_in_one.xsd :
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ex="http://example.org/schema/"
targetNamespace="http://example.org/schema/"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
<xs:include schemaLocation="relative/path/to/a.xsd"></xs:include>
<xs:include schemaLocation="relative/path/to/b.xsd"></xs:include>
<xs:include schemaLocation="relative/path/to/c.xsd"></xs:include>
</xs:schema>

JAXB Unmarshalling not working. Expected elements are (none)

I am trying to unmarshal an XML.
This is what my XML looks like
<DeviceInventory2Response xmlns="http://tempuri.org/">
<DeviceInventory2Result xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Obj123 xmlns="">
<Id>1</Id>
<Name>abc</Name>
</Obj123>
<Obj456 xmlns="">
.
.
.
I am trying to get Id and Name under Obj123. However when I run my unmarshal command I get the following error.
An Error: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://tempuri.org/", local:"DeviceInventory2Response"). Expected elements are (none)
My code looks like this in the main class:
Obj123 myObj123 = (Obj123) unmarshaller.unmarshal(inputSource);
And my class for Obj123 looks like this:
package com.myProj.pkg;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name="Obj123")
public class Obj123 {
private String Id;
private String Name;
public String getId() {
return Id;
}
public String getName() {
return Name;
}
}
I thought by setting my XMLRootElement that I should be able to skip the first 2 lines of my XML but that doesn't seem to be happening. Any ideas?
Edit:
This is how my JAXB Context is made:
JAXBContext jaxbContext = JAXBContext.newInstance();
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Obj123 obj123 = (Obj123) unmarshaller.unmarshal(xmlStreamReader);
I solved the problem by adding
#XmlRootElement(name="abc_xxx") to the Root class.(where abc_XXX is the root tag of your XML)
The JAXB classes generated by eclipse didn't add this annotation to my root class.
JAXB implementations will try to match on the root element of the document (not on a child element).
If you want to unmarshal to the middle of an XML document then you can parse the document with StAX advance the XMLStreamReader to the desired element and then unmarshal that.
For More Information
http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html
UPDATE
now I am getting the following error. An Error:
javax.xml.bind.UnmarshalException - with linked exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"",
local:"Obj123"). Expected elements are (none)].
A JAXBContext only knows about the classes you tell it about. Instead of:
JAXBContext jaxbContext = JAXBContext.newInstance();
You need to do:
JAXBContext jaxbContext = JAXBContext.newInstance(Obj123.class);
Use ObjectFactory class instead like
JAXBContext jaxbContext = null;
try {
jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
} catch (JAXBException e) {
e.printStackTrace();
}
JAXBElement<ObjectFactory> applicationElement = null;
try {
applicationElement = (JAXBElement<ObjectFactory>)
unmarshaller.unmarshal(Thread.currentThread().getClass()
.getResourceAsStream(fileName));
} catch (JAXBException e) {
e.printStackTrace();
}
Try this and will resolve above problem. My problem has been resolved.

JAXB/xjc: Generate classes from child elements and load them based on class types

I have a configuration file in XML, which looks something like:
<configuration>
<database>
<host></host>
<port></port>
</database>
<queue>
<host></host>
<port></port>
<type></type>
</queue>
</configuration>
I would like to use JAXB/xjc to generate Java classes for this configuration, however I would like to generate these classes and unmarshal these one level into the tree. Rather than receiving a Configuration.java, I would like a Database.java and Queue.java (in order that these can be separately injected into a Guice managed application). I don't (currently) see any way of doing this, however may be searching for the wrong things.
After some experimentation, I have found a solution to generate these classes and be able to populate and return these based on class:
First, I added a bindings.xjb file which will generate the contained classes (Database and Queue in this example)
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
</jaxb:bindings>
However, JAXB can't unmarshall using the Database or Queue class, only the Configuration class (this is where I may have missed something). I can do
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarhal(xmlFile);
but not
JAXBContext context = JAXBContext.newInstance(Database.class);
Unmarshaller um = context.createUnmarshaller();
Database db = (Database) um.unmarhal(xmlFile);
However, because I can get the database object by calling getDatabase() on an instance of the Configuration object, this can also be made generic using reflection (making this code cache results in the appropriate places is another exercise):
T item = null;
try {
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarshal(xmlFile);
Method[] allMethods = Configuration.class.getDeclaredMethods();
for (Method method : allMethods)
{
if (method.getReturnType().equals(clazz))
{
item = (T) method.invoke(conf);
break;
}
}
} catch (JAXBException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ConfigException("Failure detected while loading configuration", e);
}
return item;
I'm not sure this is the best solution (I only started working with JAXB yesterday), but seems to fulfill my goal.
That particular XML would correspond to a Configuration class that had properties of type Database and Queue. Isn't this what you are looking for?
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Configuration {
private Database database;
private Queue queue;
}
I am answering this question with my solution to the problem as discussed in the question. This meets my requirements:
After some experimentation, I have found a solution to generate these classes and be able to populate and return these based on class:
First, I added a bindings.xjb file which will generate the contained classes (Database and Queue in this example)
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
</jaxb:bindings>
However, JAXB can't unmarshall using the Database or Queue class, only the Configuration class (this is where I may have missed something). I can do
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarhal(xmlFile);
but not
JAXBContext context = JAXBContext.newInstance(Database.class);
Unmarshaller um = context.createUnmarshaller();
Database db = (Database) um.unmarhal(xmlFile);
However, because I can get the database object by calling getDatabase() on an instance of the Configuration object, this can also be made generic using reflection (making this code cache results in the appropriate places is another exercise):
T item = null;
try {
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarshal(xmlFile);
Method[] allMethods = Configuration.class.getDeclaredMethods();
for (Method method : allMethods)
{
if (method.getReturnType().equals(clazz))
{
item = (T) method.invoke(conf);
break;
}
}
} catch (JAXBException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ConfigException("Failure detected while loading configuration", e);
}
return item;
This allows me to get an unmarshalled Database by passing Database.class without needing to hardcode methods for ever configuration parameter. These can then be injected where needed without needing to inject the entire unmarshalled XML file.
Step1: Create 3 classes configuration.java, database.java, queue.java
configuration.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "configuration")
public class configuration{
#XmlElement
private database db;
#XmlElement
private queue q;
...
}
#XmlAccessorType(XmlAccessType.FIELD)
public class database{
#XmlElement
private String host;
#XmlElement
private String port;
....
}
-------------------------
InputStream xmlStream = new FileInputStream(config.xml);
JAXBContext jaxbContext = JAXBContext.newInstance(configuration.class, database.class,queue.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
configuration config= (configuration) jaxbUnmarshaller.unmarshal(xmlStream);
config.getDatabase().getHost(); //returns the host
config.getDatabase().getPort(); //returns the port
------------------------------------

Java xsd validation of xml without namespace

I want to validate an XML file against an XSD schema. The XML files root element does not have any namespace or xsi details. It has no attributes so just <root>.
I have tried the following code from http://www.ibm.com/developerworks/xml/library/x-javaxmlvalidapi.html with no luck as I receive
cvc-elt.1: Cannot find the declaration of element 'root'
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
File schemaFile = new File("schema.xsd");
Schema xsdScheme = factory.newSchema(schemaFile);
Validator validator = xsdScheme.newValidator();
Source source = new StreamSource(xmlfile);
validator.validate(source);
The xml validates fine with the namespace headers included etc (added via xmlspy), but I would have thought the xml namespace could be declared without having to manually edit the source file?
Edit and Solution:
public static void validateAgainstXSD(File file) {
try {
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
File schemaFile = new File("path/to/xsd");
Schema xsdScheme = factory.newSchema(schemaFile);
Validator validator = xsdScheme.newValidator();
SAXSource source = new SAXSource(
new NamespaceFilter(XMLReaderFactory.createXMLReader()),
new InputSource(new FileInputStream(file)));
validator.validate(source,null);
} catch (Exception e) {
e.printStackTrace();
}
}
protected static class NamespaceFilter extends XMLFilterImpl {
String requiredNamespace = "namespace";
public NamespaceFilter(XMLReader parent) {
super(parent);
}
#Override
public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException {
if(!arg0.equals(requiredNamespace))
arg0 = requiredNamespace;
super.startElement(arg0, arg1, arg2, arg3);
}
}
You have two separate concerns you need to take care of:
Declaring the namespace that your document uses.
Putting an xsi:schemaLocation attribute in the file to give a hint (!) where the schema is.
You can safely skip the second part, as the location is really only a hint. You cannot skip the first part. The namespace declared in the XML file is matched against the schema. Important, this:
<xml> ... </xml>
Is not the same as this:
<xml xmlns="urn:foo"> ... </xml>
So you need to declare your namespace in the XML document, otherwise it will not correspond to your schema and you will get this error.

How to validate an XML file against an XSD file?

I'm generating some xml files that needs to conform to an xsd file that was given to me. How should I verify they conform?
The Java runtime library supports validation. Last time I checked this was the Apache Xerces parser under the covers. You should probably use a javax.xml.validation.Validator.
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import java.net.URL;
import org.xml.sax.SAXException;
//import java.io.File; // if you use File
import java.io.IOException;
...
URL schemaFile = new URL("http://host:port/filename.xsd");
// webapp example xsd:
// URL schemaFile = new URL("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd");
// local file example:
// File schemaFile = new File("/location/to/localfile.xsd"); // etc.
Source xmlFile = new StreamSource(new File("web.xml"));
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(xmlFile);
System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
System.out.println(xmlFile.getSystemId() + " is NOT valid reason:" + e);
} catch (IOException e) {}
The schema factory constant is the string http://www.w3.org/2001/XMLSchema which defines XSDs. The above code validates a WAR deployment descriptor against the URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd but you could just as easily validate against a local file.
You should not use the DOMParser to validate a document (unless your goal is to create a document object model anyway). This will start creating DOM objects as it parses the document - wasteful if you aren't going to use them.
Here's how to do it using Xerces2. A tutorial for this, here (req. signup).
Original attribution: blatantly copied from here:
import org.apache.xerces.parsers.DOMParser;
import java.io.File;
import org.w3c.dom.Document;
public class SchemaTest {
public static void main (String args[]) {
File docFile = new File("memory.xml");
try {
DOMParser parser = new DOMParser();
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setProperty(
"http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
"memory.xsd");
ErrorChecker errors = new ErrorChecker();
parser.setErrorHandler(errors);
parser.parse("memory.xml");
} catch (Exception e) {
System.out.print("Problem parsing the file.");
}
}
}
We build our project using ant, so we can use the schemavalidate task to check our config files:
<schemavalidate>
<fileset dir="${configdir}" includes="**/*.xml" />
</schemavalidate>
Now naughty config files will fail our build!
http://ant.apache.org/manual/Tasks/schemavalidate.html
Since this is a popular question, I will point out that java can also validate against "referred to" xsd's, for instance if the .xml file itself specifies XSD's in the header, using xsi:schemaLocation or xsi:noNamespaceSchemaLocation (or xsi for particular namespaces) ex:
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
...
or schemaLocation (always a list of namespace to xsd mappings)
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/my_namespace http://www.example.com/document.xsd">
...
The other answers work here as well, because the .xsd files "map" to the namespaces declared in the .xml file, because they declare a namespace, and if matches up with the namespace in the .xml file, you're good. But sometimes it's convenient to be able to have a custom resolver...
From the javadocs: "If you create a schema without specifying a URL, file, or source, then the Java language creates one that looks in the document being validated to find the schema it should use. For example:"
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();
and this works for multiple namespaces, etc.
The problem with this approach is that the xmlsns:xsi is probably a network location, so it'll by default go out and hit the network with each and every validation, not always optimal.
Here's an example that validates an XML file against any XSD's it references (even if it has to pull them from the network):
public static void verifyValidatesInternalXsd(String filename) throws Exception {
InputStream xmlStream = new new FileInputStream(filename);
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(xmlStream));
xmlStream.close();
}
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);
}
}
You can avoid pulling referenced XSD's from the network, even though the xml files reference url's, by specifying the xsd manually (see some other answers here) or by using an "XML catalog" style resolver. Spring apparently also can intercept the URL requests to serve local files for validations. Or you can set your own via setResourceResolver, ex:
Source xmlFile = new StreamSource(xmlFileLocation);
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema();
Validator validator = schema.newValidator();
validator.setResourceResolver(new LSResourceResolver() {
#Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
InputSource is = new InputSource(
getClass().getResourceAsStream(
"some_local_file_in_the_jar.xsd"));
// or lookup by URI, etc...
return new Input(is); // for class Input see
// https://stackoverflow.com/a/2342859/32453
}
});
validator.validate(xmlFile);
See also here for another tutorial.
I believe the default is to use DOM parsing, you can do something similar with SAX parser that is validating as well saxReader.setEntityResolver(your_resolver_here);
Using Java 7 you can follow the documentation provided in package description.
// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);
// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();
// validate the DOM tree
try {
validator.validate(new StreamSource(new File("instance.xml"));
} catch (SAXException e) {
// instance document is invalid!
}
If you have a Linux-Machine you could use the free command-line tool SAXCount. I found this very usefull.
SAXCount -f -s -n my.xml
It validates against dtd and xsd.
5s for a 50MB file.
In debian squeeze it is located in the package "libxerces-c-samples".
The definition of the dtd and xsd has to be in the xml! You can't config them separately.
With JAXB, you could use the code below:
#Test
public void testCheckXmlIsValidAgainstSchema() {
logger.info("Validating an XML file against the latest schema...");
MyValidationEventCollector vec = new MyValidationEventCollector();
validateXmlAgainstSchema(vec, inputXmlFileName, inputXmlSchemaName, inputXmlRootClass);
assertThat(vec.getValidationErrors().isEmpty(), is(expectedValidationResult));
}
private void validateXmlAgainstSchema(final MyValidationEventCollector vec, final String xmlFileName, final String xsdSchemaName, final Class<?> rootClass) {
try (InputStream xmlFileIs = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);) {
final JAXBContext jContext = JAXBContext.newInstance(rootClass);
// Unmarshal the data from InputStream
final Unmarshaller unmarshaller = jContext.createUnmarshaller();
final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final InputStream schemaAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsdSchemaName);
unmarshaller.setSchema(sf.newSchema(new StreamSource(schemaAsStream)));
unmarshaller.setEventHandler(vec);
unmarshaller.unmarshal(new StreamSource(xmlFileIs), rootClass).getValue(); // The Document class is the root object in the XML file you want to validate
for (String validationError : vec.getValidationErrors()) {
logger.trace(validationError);
}
} catch (final Exception e) {
logger.error("The validation of the XML file " + xmlFileName + " failed: ", e);
}
}
class MyValidationEventCollector implements ValidationEventHandler {
private final List<String> validationErrors;
public MyValidationEventCollector() {
validationErrors = new ArrayList<>();
}
public List<String> getValidationErrors() {
return Collections.unmodifiableList(validationErrors);
}
#Override
public boolean handleEvent(final ValidationEvent event) {
String pattern = "line {0}, column {1}, error message {2}";
String errorMessage = MessageFormat.format(pattern, event.getLocator().getLineNumber(), event.getLocator().getColumnNumber(),
event.getMessage());
if (event.getSeverity() == ValidationEvent.FATAL_ERROR) {
validationErrors.add(errorMessage);
}
return true; // you collect the validation errors in a List and handle them later
}
}
One more answer: since you said you need to validate files you are generating (writing), you might want to validate content while you are writing, instead of first writing, then reading back for validation. You can probably do that with JDK API for Xml validation, if you use SAX-based writer: if so, just link in validator by calling 'Validator.validate(source, result)', where source comes from your writer, and result is where output needs to go.
Alternatively if you use Stax for writing content (or a library that uses or can use stax), Woodstox can also directly support validation when using XMLStreamWriter. Here's a blog entry showing how that is done:
If you are generating XML files programatically, you may want to look at the XMLBeans library. Using a command line tool, XMLBeans will automatically generate and package up a set of Java objects based on an XSD. You can then use these objects to build an XML document based on this schema.
It has built-in support for schema validation, and can convert Java objects to an XML document and vice-versa.
Castor and JAXB are other Java libraries that serve a similar purpose to XMLBeans.
Using Woodstox, configure the StAX parser to validate against your schema and parse the XML.
If exceptions are caught the XML is not valid, otherwise it is valid:
// create the XSD schema from your schema file
XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA);
XMLValidationSchema validationSchema = schemaFactory.createSchema(schemaInputStream);
// create the XML reader for your XML file
WstxInputFactory inputFactory = new WstxInputFactory();
XMLStreamReader2 xmlReader = (XMLStreamReader2) inputFactory.createXMLStreamReader(xmlInputStream);
try {
// configure the reader to validate against the schema
xmlReader.validateAgainst(validationSchema);
// parse the XML
while (xmlReader.hasNext()) {
xmlReader.next();
}
// no exceptions, the XML is valid
} catch (XMLStreamException e) {
// exceptions, the XML is not valid
} finally {
xmlReader.close();
}
Note: If you need to validate multiple files, you should try to reuse your XMLInputFactory and XMLValidationSchema in order to maximize the performance.
Are you looking for a tool or a library?
As far as libraries goes, pretty much the de-facto standard is Xerces2 which has both C++ and Java versions.
Be fore warned though, it is a heavy weight solution. But then again, validating XML against XSD files is a rather heavy weight problem.
As for a tool to do this for you, XMLFox seems to be a decent freeware solution, but not having used it personally I can't say for sure.
Validate against online schemas
Source xmlFile = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("your.xml"));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource("your.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlFile);
Validate against local schemas
Offline XML Validation with Java
I had to validate an XML against XSD just one time, so I tried XMLFox. I found it to be very confusing and weird. The help instructions didn't seem to match the interface.
I ended up using LiquidXML Studio 2008 (v6) which was much easier to use and more immediately familiar (the UI is very similar to Visual Basic 2008 Express, which I use frequently). The drawback: the validation capability is not in the free version, so I had to use the 30 day trial.

Categories

Resources