How do I validate XML against XSD in Java?

The javax.xml.validation package provides an API for XML documents validation. The validation process verify that an XML document is an instance of a specified XML schema file or XSD file. In this example we are going to validate if the records.xml file below ins an instance of the records.xsd schema. First we will create the following XML file and an XSD file it should follow.

The XML file:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <records>
        <record>
            <title>Brand New Eyes</title>
            <artist>Paramore</artist>
            <genre>Punk Rock</genre>
            <year>2011</year>
        </record>
        <record>
            <artist>Various Artist</artist>
            <genre>Rock</genre>
            <year/>
        </record>
    </records>
</root>

The XSD file:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified"
           elementFormDefault="qualified">
    <xs:element name="root" type="rootType">
    </xs:element>

    <xs:complexType name="rootType">
        <xs:sequence>
            <xs:element name="records" type="recordsType"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="recordsType">
        <xs:sequence>
            <xs:element name="record" type="recordType" maxOccurs="unbounded" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="recordType">
        <xs:sequence>
            <xs:element type="xs:string" name="title"/>
            <xs:element type="xs:string" name="artist"/>
            <xs:element type="xs:string" name="genre"/>
            <xs:element type="xs:short" name="year"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

The code snippet below will handle the validation process in the following steps. In the main() method we create the XMLValidator instance and call the validate() method and pass the XML file and the XSD file. Our validate() method start by creating an instance of SchemaFactory. The SchemaFactory.newInstance() method return an instance of SchemaFactory. In this example we are creating a W3C XML Schema.

The next step is to create a Schema object by calling the schemaFactory.newSchema() and pass the schema / XSD file. The Schema object will allow us to create an instance of javax.xml.validation.Validator by calling the schema.newValidator() method. And finally to validate if the XML is valid we call validator.validate() method and pass the XML file to be validated. If the XML is not valid, this validate() method will throw exceptions.

The Java code:

package org.kodejava.xml;

import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;

public class XMLValidator {
    public static final String XML_FILE = "records.xml";
    public static final String SCHEMA_FILE = "records.xsd";

    public static void main(String[] args) {
        XMLValidator XMLValidator = new XMLValidator();
        boolean valid = XMLValidator.validate(XML_FILE, SCHEMA_FILE);

        System.out.printf("%s validation = %b.", XML_FILE, valid);
    }

    private boolean validate(String xmlFile, String schemaFile) {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        try {
            Schema schema = schemaFactory.newSchema(new File(getResource(schemaFile)));

            Validator validator = schema.newValidator();
            validator.validate(new StreamSource(new File(getResource(xmlFile))));
            return true;
        } catch (SAXException | IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    private String getResource(String filename) throws FileNotFoundException {
        URL resource = getClass().getClassLoader().getResource(filename);
        Objects.requireNonNull(resource);

        return resource.getFile();
    }
}

How to map a bean property to an XML attribute in JAXB?

In this example you will learn how to define a bean’s / pojo’s properties as an XML attribute in JAXB. To define properties as an XML attribute we use the @XmlAttribute annotation. In the Student class below the id property will be defined as an attribute of the student root element. While the name and grade property will be an element of the student.

package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Student {
    private Integer id;
    private String name;
    private Integer grade;

    @XmlAttribute
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getGrade() {
        return grade;
    }

    public void setGrade(Integer grade) {
        this.grade = grade;
    }
}

Here the program that we can use to convert the Student bean into an XML document.

package org.kodejava.xml;

import org.kodejava.xml.support.Student;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.FileWriter;
import java.io.IOException;

public class JAXBElementAttribute {
    public static void main(String[] args) {
        Student student = new Student();
        student.setId(1);
        student.setName("Alice");
        student.setGrade(12);

        try {
            JAXBContext context = JAXBContext.newInstance(Student.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(student, new FileWriter("Student.xml"));
        } catch (JAXBException | IOException e) {
            e.printStackTrace();
        }
    }
}

When you run the program a file named Student.xml will be created with the following content:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student id="1">
    <grade>12</grade>
    <name>Alice</name>
</student>

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How to generate a wrapper element around XML representation in JAXB?

In this example you’ll learn how to use the JAXB @XmlElementWrapper annotation. This annotation can be used to generate a wrapper element around an XML element representation. When no name defined, the @XmlElementWrapper annotation uses the property name as the wrapper element name. Let’s see the code snippet below.

Here is the code snippet for marshaling the Record object into an XML document. Before the marshalling take place, we create an object of Record and adds a some Track into it.

package org.kodejava.xml;

import org.kodejava.xml.support.Track;
import org.kodejava.xml.support.Record;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class JAXBElementWrapper {
    public static void main(String[] args) {
        Track track1 = new Track();
        track1.setId(1);
        track1.setTitle("Love Me Do");

        Track track2 = new Track();
        track2.setId(2);
        track2.setTitle("From Me To You");

        Track track3 = new Track();
        track3.setId(3);
        track3.setTitle("She Loves You");

        Record record = new Record();
        record.setId(1);
        record.setTitle("The Beatles 1");
        record.getTracks().add(track1);
        record.getTracks().add(track2);
        record.getTracks().add(track3);

        try {
            JAXBContext context = JAXBContext.newInstance(Record.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(record, System.out);

            try (Writer writer = new FileWriter("Record.xml")) {
                marshaller.marshal(record, writer);
            }
        } catch (JAXBException | IOException e) {
            e.printStackTrace();
        }
    }
}
package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

@XmlRootElement
public class Record {
    private Integer id;
    private String title;
    private String artist;
    private String genre;
    private Integer year;
    private List<Track> tracks = new ArrayList<>();

    public Record() {
    }

    @XmlAttribute
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public Integer getYear() {
        return year;
    }

    public void setYear(Integer year) {
        this.year = year;
    }

    @XmlElementWrapper(name = "trackList")
    @XmlElement(name = "track")
    public List<Track> getTracks() {
        return tracks;
    }

    public void setTracks(List<Track> tracks) {
        this.tracks = tracks;
    }

    @Override
    public String toString() {
        return "Record{" +
                "title='" + title + "'\n" +
                ", artist='" + artist + "'\n" +
                ", genre='" + genre + "'\n" +
                ", year=" + year +
                '}';
    }
}
package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Track {
    private Integer id;
    private String title;

    public Track() {
    }

    public Integer getId() {
        return id;
    }

    @XmlAttribute
    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Track{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

The above program will generated the following XML document.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<record id="1">
    <title>The Beatles 1</title>
    <trackList>
        <track id="1">
            <title>Love Me Do</title>
        </track>
        <track id="2">
            <title>From Me To You</title>
        </track>
        <track id="3">
            <title>She Loves You</title>
        </track>
    </trackList>
</record>

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How to convert object to XML using JAXB?

JAXB, Java Architecture for XML Binding, it uses JAXB annotations to convert POJO to or from XML file. In this example you will learn how to convert an object / POJO into an XML document using JAXB. The process of converting an object into XML also known as marshalling. In this snippet our POJO is a class called Track as you can see below.

The first thing that we need to do is to annotate the Track POJO with a couple of JAXB annotations. For this example we add the @XmlRootElement, @XmlElement and @XmlAttribute annotations.

package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Track {
    private Integer id;
    private String title;

    public Track() {
    }

    public Integer getId() {
        return id;
    }

    @XmlAttribute
    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Track{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

After we have the Track class annotated we can use the code snippet below to convert the POJO into XML. The step to take is to start by creating an instance of JAXBContext. From this context object we create the Marshaller object. To convert it to XML we called the marshall() method. The method have many overloads, in this case we just want to print the XML to the System.out.

package org.kodejava.xml;

import org.kodejava.xml.support.Track;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBObjectToXml {
    public static void main(String[] args) {
        Track track = new Track();
        track.setId(1);
        track.setTitle("Hey Jude");

        try {
            JAXBContext context = JAXBContext.newInstance(Track.class);

            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(track, System.out);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

If you run this code you will get the following output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<track id="1">
    <title>Hey Jude</title>
</track>

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How to create an XML file of a POJO using JAXB?

The code snippet below show you how to convert POJO into XML file using JAXB. To do this we can pass the output file where we want the XML to be saved to the marshaller object.

package org.kodejava.xml;

import org.kodejava.xml.support.Track;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;

public class JAXBObjectToXmlFile {
    public static void main(String[] args) {
        Track track = new Track();
        track.setId(2);
        track.setTitle("She Loves You");

        try {
            JAXBContext context = JAXBContext.newInstance(Track.class);

            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            File output = new File("Track.xml");
            marshaller.marshal(track, output);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

This snippet will create a file called Track.xml with the following content:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<track id="2">
    <title>She Loves You</title>
</track>
package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Track {
    private Integer id;
    private String title;

    public Track() {
    }

    public Integer getId() {
        return id;
    }

    @XmlAttribute
    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Track{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How to convert an XML file into object using JAXB?

In this code snippet you can learn how to convert or unmarshall an XML file into it corresponding POJO. The steps on unmarshalling XML to object begin by creating an instance of JAXBContext. With the context object we can then create an instance of Unmarshaller class. Using the unmarshall() method and pass an XML file will give us the target POJO as the result.

Let’s see the code snippet below:

package org.kodejava.xml;

import org.kodejava.xml.support.Track;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.net.URL;
import java.util.Objects;

public class JAXBXmlToObject {
    public static void main(String[] args) {
        try {
            URL resource = JAXBXmlToObject.class.getResource("/Track.xml");
            File file = new File(Objects.requireNonNull(resource).toURI());
            JAXBContext context = JAXBContext.newInstance(Track.class);

            Unmarshaller unmarshaller = context.createUnmarshaller();
            Track track = (Track) unmarshaller.unmarshal(file);

            System.out.println("Track = " + track);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Here is the context of Track.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<track id="2">
    <title>She Loves You</title>
</track>
package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Track {
    private Integer id;
    private String title;

    public Track() {
    }

    public Integer getId() {
        return id;
    }

    @XmlAttribute
    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Track{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How do I define the XML element order in JAXB?

In this code snippet you will learn how to define the order of XML element generated by the JAXB API. To define the element order we need to use the @XmlType annotation in our POJO. This annotation propOrder attribute is where we define what element should come first and which element should be place at the end.

The XML generated by the marshaller below will have the following order: street, city, province, zipcode and country as the last XML element.

package org.kodejava.xml.support;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

public @XmlRootElement(name = "customerAddress")
@XmlType(propOrder = {"street", "city", "province", "zipcode", "country"})
class Address {
    private String street;
    private String city;
    private String province;
    private String country;
    private String zipcode;

    @XmlElement
    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    @XmlElement
    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @XmlElement
    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    @XmlElement
    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @XmlElement
    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipCode) {
        this.zipcode = zipCode;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                ", province='" + province + '\'' +
                ", country='" + country + '\'' +
                ", zipcode='" + zipcode + '\'' +
                '}';
    }
}

Now, let’s create a program to run the marshalling process of the Address POJO to convert it into XML document:

package org.kodejava.xml;

import org.kodejava.xml.support.Address;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBElementOrder {
    public static void main(String[] args) {
        Address address = new Address();
        address.setStreet("Sunset Road");
        address.setCity("Denpasar");
        address.setProvince("Bali");
        address.setCountry("Indonesia");
        address.setZipcode("80225");

        try {
            JAXBContext context = JAXBContext.newInstance(Address.class);

            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.marshal(address, System.out);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

And it will give you the following output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customerAddress>
    <street>Sunset Road</street>
    <city>Denpasar</city>
    <province>Bali</province>
    <zipcode>80225</zipcode>
    <country>Indonesia</country>
</customerAddress>

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How do I change the XML root element name in JAXB?

In the following code snippet you will learn how to change the default root element name of the XML generated by the JAXB API. By default, the name of the class is use as the root element name. To change the root element name we can use the name property of the @XmlRootElement annotation. In the Customer model below we change the root element name into cust.

package org.kodejava.xml;

import org.kodejava.xml.support.Address;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "cust")
@XmlType(propOrder = {"id", "name", "address"})
public class Customer {
    private Integer id;
    private String name;
    private Address address;

    @XmlElement
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlElement
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}
package org.kodejava.xml.support;

public class Address {
    private String street;
    private String city;
    private String province;
    private String zipcode;
    private String country;

    // Getters & Setters
}
package org.kodejava.xml;

import org.kodejava.xml.support.Address;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBCustomRootElement {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.setId(1);
        customer.setName("Johnny Mnemonic");

        Address address = new Address();
        address.setStreet("Sunset Road");
        address.setCity("Denpasar");
        address.setProvince("Bali");
        address.setZipcode("80225");
        address.setCountry("Indonesia");
        customer.setAddress(address);

        try {
            JAXBContext context = JAXBContext.newInstance(Customer.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.marshal(customer, System.out);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

When we convert this POJO to XML using JAXB API we will get the following result:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cust>
    <id>1</id>
    <name>Johnny Mnemonic</name>
    <address>
        <city>Denpasar</city>
        <country>Indonesia</country>
        <province>Bali</province>
        <street>Sunset Road</street>
        <zipcode>80225</zipcode>
    </address>
</cust>

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>    
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-ri</artifactId>
        <version>2.3.5</version>
        <type>pom</type>
    </dependency>
</dependencies>

Maven Central Maven Central

How do I build object from XML file using SAX?

This example will show you how to parse an XML file using SAX parser and build an object graph from the parsed XML. We will read the records.xml file that contains some recording information and create the Record object from it.

The DefaultHandler in this example created as an anonymous class. We override some method related to the ContentHandler interface such as the startElement, endElement and characters methods.

package org.kodejava.xml;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.kodejava.xml.support.Record;

public class SAXDataDemo {
    // List of our records.
    private final List<Record> records = new ArrayList<>();

    // Stacks for storing the elements and objects.
    private final Stack<String> elements = new Stack<>();
    private final Stack<Record> objects = new Stack<>();

    public static void main(String[] args) {
        SAXDataDemo demo = new SAXDataDemo();
        demo.run();
    }

    private void run() {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();

            String filename = "/records.xml";
            InputStream is = getClass().getResourceAsStream(filename);

            parser.parse(is, new DefaultHandler() {
                @Override
                public void startElement(String uri, String localName, String qName,
                                         Attributes attributes) throws SAXException {
                    elements.push(qName);
                    if ("record".equals(qName)) {
                        Record record = new Record();
                        objects.push(record);
                        records.add(record);
                    }
                }

                @Override
                public void endElement(String uri, String localName,
                                       String qName) throws SAXException {
                    elements.pop();
                }

                @Override
                public void characters(char[] ch, int start, int length)
                        throws SAXException {

                    String value = new String(ch, start, length);
                    if (value.length() == 0) {
                        return;
                    }

                    if ("title".equals(currentElement())) {
                        Record record = objects.peek();
                        record.setTitle(value);
                    } else if ("artist".equals(currentElement())) {
                        Record record = objects.peek();
                        record.setArtist(value);
                    } else if ("genre".equals(currentElement())) {
                        Record record = objects.peek();
                        record.setGenre(value);
                    } else if ("year".equals(currentElement())) {
                        Record record = objects.peek();
                        record.setYear(Integer.parseInt(value));
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        for (Record record : records) {
            System.out.println("record = " + record);
        }
    }

    private String currentElement() {
        return elements.peek();
    }
}

The Record class.

package org.kodejava.xml.support;

public class Record {
    private String title;
    private String artist;
    private String genre;
    private int year;

    public Record() {
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "Record{" +
                "title='" + title + "'\n" +
                ", artist='" + artist + "'\n" +
                ", genre='" + genre + "'\n" +
                ", year=" + year +
                '}';
    }
}

The following XML is the content of our records.xml file.

<?xml version="1.0"?>
<root>
    <records>
        <record>
            <title>Brand New Eyes</title>
            <artist>Paramore</artist>
            <genre>Punk Rock</genre>
            <year>2011</year>
        </record>
        <record>
            <title>Rock Beatles</title>
            <artist>Various Artist</artist>
            <genre>Rock</genre>
            <year>2010</year>
        </record>
    </records>
</root>

When we run this example will get the following output:

record = Record{title='Brand New Eyes'
, artist='Paramore'
, genre='Punk Rock'
, year=2011}
record = Record{title='Rock Beatles'
, artist='Various Artist'
, genre='Rock'
, year=2010}

How do I parse an XML file using SAX?

This example show you how to read / parse an xml file using the SAX (Simple API for XML) parser. In the main class (SAXDemo) we create the instance of SAXParserFactory and the SAXParser. The SAXParser.parse() method will parse the given InputStream and handle the xml document using the SAXHandler class that we created.

package org.kodejava.xml;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.InputStream;

public class SAXDemo {
    public static void main(String[] args) throws Exception {
        // Creates a new instance of SAXParserFactory that in turn
        // creates a SAXParser.
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        // The handler that will listen to the SAX event during
        // the xml traversal.
        SAXHandler handler = new SAXHandler();
        InputStream data = SAXDemo.class.getResourceAsStream("/person.xml");
        parser.parse(data, handler);
    }
}

The SAXHandler class extended from the org.xml.sax.helpers.DefaultHandler class. The handler will listen to the event triggered by the SAXParser. This handler methods is defined by the interfaces such as the ContentHandler, ErrorHandler, DTDHandler, and EntityResolver.

For example to read the content of the XML file there are methods to listen to events such as startDocument, endDocument, startElement, endElement, etc, which defined by the ContentHandler interface.

package org.kodejava.xml;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SAXHandler extends DefaultHandler {
    @Override
    public void startDocument() throws SAXException {
        System.out.println("startDocument");
    }

    @Override
    public void endDocument() throws SAXException {
        System.out.println("endDocument");
    }

    @Override
    public void startElement(String uri, String localName,
                             String qName, Attributes attributes)
            throws SAXException {
        System.out.println("startElement: " + qName);
    }

    @Override
    public void endElement(String uri, String localName,
                           String qName) throws SAXException {
        System.out.println("endElement");
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        System.out.println("characters  : " + new String(ch, start, length));
    }
}

Here an example of the xml file will be read by our program:

<root>
    <persons>
        <person>
            <name>Foo</name>
        </person>
        <person>
            <name>Bar</name>
        </person>
    </persons>
</root>

Our program will print the following output:

startDocument
startElement: root
characters  : 

startElement: persons
characters  : 

startElement: person
characters  : 

startElement: name
characters  : Foo
endElement
characters  : 

endElement
characters  : 

startElement: person
characters  : 

startElement: name
characters  : Bar
endElement
characters  : 

endElement
characters  : 

endElement
characters  : 

endElement
endDocument