JAXB stands for Java Architecture for XML Binding. It is used to convert XML to java object and java object to XML. JAXB provides an API for reading and writing Java objects to and from XML documents.
JAXB is memory efficient and provides fast and efficient way to bind XML and Java objects.
JAXB provides below functionalities,
- Unmarshalling: Reading XML documents to JAVA content tree
- Marshalling: Writing Java content tree to XML document
- Schema Generation: JAXB provide ways to generate XML schema from Java objects
- Apart from this, it provides way to validate. Validation is the process of verifying that XML document meets all the constraints mentioned in schema. JAXB 2.0 provides way to validate at marshalling and unmarshalling time. Validation at marshalling time is very useful especially in web service where user don’t invalidate XML document when modifying the document in JAXB.
In this tutorial, we will see how to create XML document from Java object (UnMarshal), Java object to XML document (Marshal), creating schema file from Java beans and how to use EventHandler for validation.
First, create model object as below,
Address:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | package in.blogspot.ashish4java.userdetails.model; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlType(propOrder = { "address_line_1", "address_line_2", "address_line_3", "address_line_4", "city" }) @XmlRootElement(name="Address") public class Address { public Address() { super(); } public Address(String city, String address_line_1, String address_line_2, String address_line_3, String address_line_4) { super(); this.city = city; this.address_line_1 = address_line_1; this.address_line_2 = address_line_2; this.address_line_3 = address_line_3; this.address_line_4 = address_line_4; } private String city; private String address_line_1; private String address_line_2; private String address_line_3; private String address_line_4; @XmlElement(name="City", nillable=false, required=true) public String getCity() { return city; } public void setCity(String city) { this.city = city; } @XmlElement(name="AddressLine1", nillable=false, required=true) public String getAddress_line_1() { return address_line_1; } public void setAddress_line_1(String address_line_1) { this.address_line_1 = address_line_1; } @XmlElement(name="AddressLine2", nillable=false, required=true) public String getAddress_line_2() { return address_line_2; } public void setAddress_line_2(String address_line_2) { this.address_line_2 = address_line_2; } @XmlElement(name="AddressLine3") public String getAddress_line_3() { return address_line_3; } public void setAddress_line_3(String address_line_3) { this.address_line_3 = address_line_3; } @XmlElement(name="AddressLine4") public String getAddress_line_4() { return address_line_4; } public void setAddress_line_4(String address_line_4) { this.address_line_4 = address_line_4; } @Override public String toString() { return "Address [city=" + city + ", address_line_1=" + address_line_1 + ", address_line_2=" + address_line_2 + ", address_line_3=" + address_line_3 + ", address_line_4=" + address_line_4 + "]"; } } |
User:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | package in.blogspot.ashish4java.userdetails.model; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlType(propOrder = { "userId", "firstName", "surName", "addressList" }) @XmlRootElement(name="User") public class User { public User() { super(); } public User(String firstName, String surName, List<Address> addressList, int userId) { super(); this.firstName = firstName; this.surName = surName; this.addressList = addressList; this.userId = userId; } private String firstName; private String surName; private List<Address> addressList; private int userId; /** * @return the firstName */ @XmlElement(name="FirstName", nillable=false, required=true) public String getFirstName() { return firstName; } /** * @param firstName * the firstName to set */ public void setFirstName(String firstName) { this.firstName = firstName; } /** * @return the surName */ @XmlElement(name="SurName", nillable=false, required=true) public String getSurName() { return surName; } /** * @param surName * the surName to set */ public void setSurName(String surName) { this.surName = surName; } /** * @return the addressList */ @XmlElementWrapper(name="Addresses", required=true, nillable=false) @XmlElement(name="Address", type=Address.class, nillable=false, required=true) public List<Address> getAddressList() { return addressList; } /** * @param addressList * the addressList to set */ public void setAddressList(List<Address> addressList) { this.addressList = addressList; } /** * @return the userId */ @XmlElement(name="UserId", nillable=false, required=true) public int getUserId() { return userId; } /** * @param userId * the userId to set */ public void setUserId(int userId) { this.userId = userId; } @Override public String toString() { return "User [firstName=" + firstName + ", surName=" + surName + ", addressList=" + addressList + ", userId=" + userId + "]"; } } |
Note down use of annotations like @XmlType to define ordering of field in XML document, @XmlElement to define whether property is required or not, nullable or not. @XmlElementWrapper is used to create wrapper around collections(list) of addresses.
Now, generate schema, marshall and unmarshall using these java beans:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | package in.blogspot.ashish4java.userdetails.helper; import java.io.File; import java.io.IOException; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.SchemaOutputResolver; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.transform.Result; import javax.xml.transform.stream.StreamResult; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.xml.sax.SAXException; import in.blogspot.ashish4java.userdetails.model.Address; import in.blogspot.ashish4java.userdetails.model.User; /** * @author admin * */ public class MarshallerAndUnMarshaller { public static final JAXBContext jc = initContext(); //Location of schema file private static final String SCHEMA_FILE = "src/in/blogspot/ashish4java/userdetails/data/UserSchema.xsd"; //location of XML document public static final String USER_XML_FILE = "src/in/blogspot/ashish4java/userdetails/data/User.xml"; private static JAXBContext initContext() { try { return JAXBContext.newInstance(User.class, Address.class); } catch (JAXBException e) { System.out.println("Exception initialising context " + e.getMessage()); } return null; } /** * Method to write to XML document from Java object. This method sets the * schema document first and uses ValidationEventHandler to validate before * writing to XML document file as formatted output. However validation * fails, unmarshalling continues for rest of document. * * @param user * java object which needs to marshall to XML document. */ public final void MarshalToXML(User user) { try { final Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(user, System.out); System.out.println(); final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); final Schema schema = schemaFactory.newSchema(new File(SCHEMA_FILE)); marshaller.setSchema(schema); marshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { System.out.println("MarshalToXML - Schema Validation Issue : Severity : " + event.getSeverity()); System.out.println("MarshalToXML - Schema Validation Issue : Message : " + event.getMessage()); return true; } }); marshaller.marshal(user, new File(USER_XML_FILE)); } catch (JAXBException | SAXException e) { System.out.println("Exception " + e.getMessage()); System.out.println("Exception " + e.toString()); } System.out.println(); System.out.println(); } /** * It takes XML document file as input and reads it to Java content tree. * Method does validation with schema before reading XML document. However * validation fails, unmarshalling continues for rest of document. * * @param File * to read to Java content tree. */ public final void UnMarshalToJava(File f) { try { final Unmarshaller unMarshaller = jc.createUnmarshaller(); final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); final Schema schema = schemaFactory.newSchema(new File(SCHEMA_FILE)); unMarshaller.setSchema(schema); unMarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { System.out.println("UnMarshalToJava - Schema Validation Issue : Severity : " + event.getSeverity()); System.out.println("UnMarshalToJava - Schema Validation Issue : Message : " + event.getMessage()); System.out.println("UnMarshalToJava - Schema Validation Issue : LineNumber : " + event.getLocator().getLineNumber()); System.out.println("UnMarshalToJava - Schame Validation Issue : ColumnNumber : " + event.getLocator().getColumnNumber()); return true; } }); final User user = (User) unMarshaller.unmarshal(f); System.out.println(); System.out.println("Unmarshlled User object is " + user); } catch (JAXBException | SAXException e) { System.out.println("Exception in UnMarshalToJava " + e.getMessage()); } } /** * This method generate the schema at specified location using Java classes * as specified in JAXBContext. */ public final void generateSchema() { try { jc.generateSchema(new SchemaOutputResolver() { @Override public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException { final File file = new File(SCHEMA_FILE); final StreamResult result = new StreamResult(file); result.setSystemId(file.toURI().toURL().toString()); return result; } }); } catch (IOException e) { System.out.println("Exception in generateSchema " + e.getMessage()); } } } |
handleEvent method of ValidationeventHandler returns true which means, unmarshalling or marshalling to continue even if there are validation error occurs.
Now, we just need to call each of above methods,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package in.blogspot.ashish4java.userdetails; import java.io.File; import java.util.ArrayList; import java.util.List; import in.blogspot.ashish4java.userdetails.helper.MarshallerAndUnMarshaller; import in.blogspot.ashish4java.userdetails.model.Address; import in.blogspot.ashish4java.userdetails.model.User; public class Main { public static void main(String[] args) { User user = createUser(); MarshallerAndUnMarshaller marshUnMarsh = new MarshallerAndUnMarshaller(); marshUnMarsh.generateSchema(); marshUnMarsh.MarshalToXML(user); marshUnMarsh .UnMarshalToJava(new File(MarshallerAndUnMarshaller.USER_XML_FILE)); } private static User createUser() { User user = new User("Ashish", null, createAddressList(), 1); return user; } private static List<Address> createAddressList() { Address address1 = new Address("Sydney", "CBD", "Australia", null, null); Address address2 = new Address("Mumbai", null, "India", null, null); Address address3 = new Address("London", "Zone1", "United kingdom", null, null); List<Address> addressList = new ArrayList<Address>(); addressList.add(address1); addressList.add(address2); addressList.add(address3); return addressList; } } |
With this, on calling executing this program, we get below output,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <User> <UserId>1</UserId> <FirstName>Ashish</FirstName> <Addresses> <Address> <AddressLine1>CBD</AddressLine1> <AddressLine2>Australia</AddressLine2> <City>Sydney</City> </Address> <Address> <AddressLine2>India</AddressLine2> <City>Mumbai</City> </Address> <Address> <AddressLine1>Zone1</AddressLine1> <AddressLine2>United kingdom</AddressLine2> <City>London</City> </Address> </Addresses> </User> MarshalToXML - Schema Validation Issue : Severity : 2 MarshalToXML - Schema Validation Issue : Message : cvc-complex-type.2.4.a: Invalid content was found starting with element 'Addresses'. One of '{SurName}' is expected. MarshalToXML - Schema Validation Issue : Severity : 2 MarshalToXML - Schema Validation Issue : Message : cvc-complex-type.2.4.a: Invalid content was found starting with element 'AddressLine2'. One of '{AddressLine1}' is expected. UnMarshalToJava - Schema Validation Issue : Severity : 2 UnMarshalToJava - Schema Validation Issue : Message : cvc-complex-type.2.4.a: Invalid content was found starting with element 'Addresses'. One of '{SurName}' is expected. UnMarshalToJava - Schema Validation Issue : LineNumber : 5 UnMarshalToJava - Schame Validation Issue : ColumnNumber : 16 UnMarshalToJava - Schema Validation Issue : Severity : 2 UnMarshalToJava - Schema Validation Issue : Message : cvc-complex-type.2.4.a: Invalid content was found starting with element 'AddressLine2'. One of '{AddressLine1}' is expected. UnMarshalToJava - Schema Validation Issue : LineNumber : 12 UnMarshalToJava - Schame Validation Issue : ColumnNumber : 27 Unmarshlled User object is User [firstName=Ashish, surName=null, addressList=[Address [city=Sydney, address_line_1=CBD, address_line_2=Australia, address_line_3=null, address_line_4=null], Address [city=Mumbai, address_line_1=null, address_line_2=India, address_line_3=null, address_line_4=null], Address [city=London, address_line_1=Zone1, address_line_2=United kingdom, address_line_3=null, address_line_4=null]], userId=1] |
There are validation errors thrown as we have not set 'SurName' field in user object and 'AddressLine2' is also null in one of Address object.
We have not covered 'xjc' which is XML to JAVA compiler to generate java classes from schema document. This is mainly used in 'contract-first' approach where schema document contract is defined first and based on that, Java classes are generated.
This is all about JAXB in this tutorial. Thanks and STAY TUNED!