Java XML Validation: How to Validate an XML Document against an XSD
Introducing our Java XML Validation Project! Master the art of validating XML effortlessly against XSD in a Java environment. Gain hands-on experience, ensuring the integrity and conformity of your data.
1. Introduction
Introducing our Java XML Validation Project! Gain hands-on experience in validating XML documents against XSD with ease. This project serves as a practical guide, equipping you with the necessary knowledge and tools to ensure the integrity and conformity of your XML data in a Java environment. Discover the simplicity of our step-by-step approach, which walks you through the entire validation process. Master the art of validating XML effortlessly and unlock a new level of confidence in your data. Join us on this journey as we empower you to harness the power of XML validation in your Java projects.
2. Generating Java Classes from XSD
For this exercise, we will be using an XSD from w3schools https://www.w3schools.com/xml/schema_example.asp.
To be able to generate the Java classes from XSD, we will be using Eclipse Enterprise Edition. IntelliJ can also do it with the Jakarta plugin, but Eclipse performs better, especially when dealing with XJB files.
IntelliJ
Eclipse JAXB Classes Generator
Generated Classes
3. Dependencies
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${jakarta.version}</version>
<scope>runtime</scope>
</dependency>
4. Core Classes
To run the validations and throw the appropriate exception or name of the tag where a constraint is violated, we will need the following classes.
4.1 ResourceResolver
To import an external XSD, for example, if we will be using the XML digital signature.
<xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd"/>
public class ResourceResolver implements LSResourceResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceResolver.class);
public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
// note: in this sample, the XSD's are expected to be in the root of the classpath
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("xsd/" + systemId);
return new Input(publicId, systemId, resourceAsStream);
}
static class Input implements LSInput {
private String publicId;
private String systemId;
public String getStringData() {
String textDataFromFile;
try {
BufferedReader bufferReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
String eachStringLine;
while ((eachStringLine = bufferReader.readLine()) != null) {
stringBuilder.append(eachStringLine).append("\n");
}
textDataFromFile = stringBuilder.toString();
return textDataFromFile;
} catch (IOException e) {
LOGGER.error("Fail reading from inputStream={}", e.getMessage());
return null;
}
}
...
4.2 SaxErrorHandler
For us to be able to know the tag where a constraint exception is thrown, we need to define a custom exception that accepts an XMLStreamReader object where we can get the localName property.
public class SaxErrorHandler implements ErrorHandler {
private XMLStreamReader reader;
public SaxErrorHandler(XMLStreamReader reader) {
this.reader = reader;
}
@Override
public void error(SAXParseException e) {
warning(e);
}
@Override
public void fatalError(SAXParseException e) {
warning(e);
}
@Override
public void warning(SAXParseException e) {
throw new XmlValidationException(reader.getLocalName());
}
}
4.3 Miscellaneous Classes
We will also be needing utility classes for:
- Creating a JAXBElement
- Convert JAXBElement to XMLStreamReader
- Defining the correct marshaller object
These classes are all available in the project.
4.4 XmlSchemaValidator Interface
And finally, the interface that pieces together all components to do the validation.
default void validateXMLSchema(String xsdPath, JAXBElement<?> xml) {
Validator validator = null;
try {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
factory.setResourceResolver(new ResourceResolver());
Schema schema = factory.newSchema(ResourceUtils.getFile(xsdPath));
validator = schema.newValidator();
XMLStreamReader xmlStreamReader = asStreamReader(xml);
ErrorHandler errorHandler = new SaxErrorHandler(xmlStreamReader);
validator.setErrorHandler(errorHandler);
validator.validate(new StAXSource(xmlStreamReader));
} catch (IOException | SAXException | JAXBException | XMLStreamException e) {
var xmlEx = getXmlValidationExceptionIfPresent(e);
throw new InvalidXmlException(xmlEx.getLocalName(), e);
}
}
5. Source Code / Git Repository
The complete source code for this project is available for my sponsors at https://github.com/czetsuyatech/java-xml-validation-by-xsd.
Sponsorship link: https://github.com/sponsors/czetsuya
Project Screenshot