How to validate XML against XSD schema
The validation process plays very important role in any system's stability and performance.
It is always a best practice to validate the request object against the predefined datatypes, length and/or structure.
In this example we are going to validate a XML request to check whether it satisfies the requirements defined in XML Schema Definition (XSD) and return the appropriate validation error message.
Maven Dependencies
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
STEP-1 : Create A Sample XML Schema Definition (XSD)
- XSD file Path src/main/resources/xsdSchema/XmlBeansSchema.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0">
<xsd:annotation>
<xsd:appinfo>
<jaxb:globalBindings optionalProperty="primitive">
<jaxb:serializable/>
</jaxb:globalBindings>
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="request">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="requestId" minOccurs="1"/>
<xsd:element ref="username" minOccurs="1"/>
<xsd:element ref="password" minOccurs="1"/>
<xsd:element ref="age" minOccurs="0"/>
<xsd:element ref="optionalData" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="requestId" type="tenStringNotNull"/>
<xsd:element name="username" type="tenStringNotNull"/>
<xsd:element name="password" type="tenStringNotNull"/>
<xsd:element name="age" type="positiveInteger"/>
<xsd:element name="requestData" type="oneHundredStringNotNull"/>
<xsd:element name="optionalData" type="tenString"/>
<xsd:simpleType name="tenString">
<xsd:restriction base="xsd:normalizedString">
<xsd:minLength value="0" />
<xsd:maxLength value="10" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="tenStringNotNull">
<xsd:restriction base="xsd:normalizedString">
<xsd:minLength value="1" />
<xsd:maxLength value="10" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="oneHundredString">
<xsd:restriction base="xsd:normalizedString">
<xsd:minLength value="0" />
<xsd:maxLength value="100" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="oneHundredStringNotNull">
<xsd:restriction base="xsd:normalizedString">
<xsd:minLength value="1" />
<xsd:maxLength value="100" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="positiveInteger">
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="0"/>
<xsd:totalDigits value="12"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
STEP-2 : Generate JAXB Classes using XSD file
-
Prerequisite to generate Java classes using XSD file
- JDK 1.8
- Eclipse
-
Generate the JAXB objects using following steps
- Right click on XSD file
- Navigate to Generate --> JAXB Classes
- Select the destination project (it can be your current project also)
- Select the folder and package where you want newly generated files. In this example package name is com.learning.xml.validation.xmlBeans
- Leave the remaining option as default and click on FINISH
STEP-3 : Create XML Validator Class
Once the Jaxb objects are generated create the XmlValidator.class
using them. The XmlValidator.class
file also contains Marshaller
and Unmarshaller
method for XML
and Java
objects.
package com.learning.xml.validation;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.learning.xml.validation.xmlBeans.Request;
public class XmlValidator implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5446213683191890340L;
private XMLStreamReader reader;
private StringBuffer errorMsg = new StringBuffer();
private static Schema schema;
private static JAXBContext jaxbContext;
static {
initJaxbContext();
}
private static JAXBContext initJaxbContext() {
try {
if (null != jaxbContext) {
return jaxbContext;
}
try {
jaxbContext = JAXBContext.newInstance("com.learning.xml.validation.xmlBeans",
Request.class.getClassLoader());
} catch (JAXBException ex) {
throw new ExceptionInInitializerError(ex);
}
} catch (Exception e) {
System.err.println("Error InitJaxbContext = " + ExceptionUtils.getStackTrace(e));
}
return jaxbContext;
}
public ThreadLocal<Unmarshaller> jaxbUnmarshaller() {
return new ThreadLocal<Unmarshaller>() {
@Override
protected synchronized Unmarshaller initialValue() {
try {
return initJaxbContext().createUnmarshaller();
} catch (JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
};
}
public ThreadLocal<Marshaller> jaxbMarshaller() {
return new ThreadLocal<Marshaller>() {
@Override
protected synchronized Marshaller initialValue() {
try {
return initJaxbContext().createMarshaller();
} catch (JAXBException e) {
throw new ExceptionInInitializerError(e);
}
}
};
}
public Schema getSchema() throws Exception {
if (null != schema) {
return schema;
}
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
InputStream resourceAsStream = getClass().getResourceAsStream("/xsdSchema/XmlBeansSchema.xsd");
StreamSource streamSource = new StreamSource(resourceAsStream);
schema = schemaFactory.newSchema(streamSource);
return schema;
}
public Validator getValidator() throws Exception{
Validator validator = null;
try {
validator = getSchema().newValidator();
} catch (Exception e) {
String message = ExceptionUtils.getStackTrace(e);
System.err.println("Error while request validation : " + message);
}
return validator;
}
public void validateRequest(String requestStr) {
try {
getValidator().validate(new StreamSource(new ByteArrayInputStream(requestStr.getBytes(StandardCharsets.UTF_8))));
} catch (Exception e) {
String message = ExceptionUtils.getStackTrace(e);
System.err.println("Error while validatation = " + message);
errorMsg = new StringBuffer();
errorMsg.append(message.substring(0, message.indexOf("\n\t")));
}
}
public ThreadLocal<Marshaller> getJaxbMarshaller() {
return jaxbMarshaller();
}
public XmlValidator(XMLStreamReader reader) {
this.reader = reader;
}
public XmlValidator() {
}
public void error(SAXParseException e) throws SAXException {
warning(e);
}
public void fatalError(SAXParseException e) throws SAXException {
warning(e);
}
public void warning(SAXParseException e) throws SAXException {
System.err.println("LocalName = " + reader.getLocalName());
String message = ExceptionUtils.getStackTrace(e);
System.err.println("Error = '" + reader.getLocalName() + "' " + message);
errorMsg = new StringBuffer();
errorMsg.append("'" + reader.getLocalName() + "' " + message.substring(0, message.indexOf("\n\t")));
}
public StringBuffer getErrorMessages() {
return errorMsg;
}
public void setErrorMessages(StringBuffer errorMessages) {
this.errorMsg = errorMessages;
}
}
STEP-4 : Create JaxbXmlXSDUtil.class
package com.learning.xml.validation;
import java.io.StringWriter;
import java.math.BigInteger;
import org.apache.commons.lang3.exception.ExceptionUtils;
import com.learning.xml.validation.xmlBeans.Request;
public class JaxbXmlXSDUtil {
public static void main(String[] args) {
try {
XmlValidator xmlValidator = new XmlValidator();
String xmlStr = getXmlAsString(xmlValidator);
System.out.println("xmlStr = " + xmlStr);
xmlValidator.validateRequest(xmlStr);
} catch (Exception e) {
System.err.println("Error : " + ExceptionUtils.getStackTrace(e));
}
}
public static String getXmlAsString(XmlValidator xmlValidator) throws Exception{
StringWriter requestStr = new StringWriter();
Request request = new Request();
request.setUsername("usernameABC");
request.setPassword("dfasfsafgsdfgsghfdghdfh");
request.setRequestId("Request11");
request.setAge(new BigInteger("10"));
xmlValidator.getJaxbMarshaller().get().marshal(request, requestStr);
return requestStr.toString();
}
}
STEP-5 : XML validation output against XSD Schema
After executing the JaxbXmlXSDUtil.class
below output is generated. It shows the Element name which contains invalid legth data.
In the schema the element username
maximum length is defined as tenStringNotNull
but the value usernameABC
contains total Eleven
character due to that validation has failed and appropriate readable message is generated.
STEP-6 : XML validation output against XSD Schema after correcting Username element value to Ten characters
In the schema the element password
maximum length is defined as tenStringNotNull
but the value dfasfsafgsdfgsghfdghdfh
contains total Twenty Three
character due to that validation has failed and appropriate readable message is generated.
Happy Learning!!!