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

    1. JDK 1.8
    2. Eclipse
  • Generate the JAXB objects using following steps

    1. Right click on XSD file
    2. Navigate to Generate --> JAXB Classes
    3. Select the destination project (it can be your current project also)
    4. Select the folder and package where you want newly generated files. In this example package name is com.learning.xml.validation.xmlBeans
    5. 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.

XSD Validation Failed


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.

xml password validation


Happy Learning!!!