DEV Community

Ed Legaspi
Ed Legaspi

Posted on • Originally published at czetsuyatech.com

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

Image description

Eclipse JAXB Classes Generator

Image description

Generated Classes

Image description

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>
Enter fullscreen mode Exit fullscreen mode

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;
      }
    }
...
Enter fullscreen mode Exit fullscreen mode

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());
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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

Image description

Top comments (0)