Hi guys. It’s been so long since I have posted anything at all on my blog, that I’m actually ashamed to call myself a blogger. I’m probably more of a blogger wanna-be at this point. In my defense, between the last post I wrote and I now, I have created, given birth to and since raised a whole entire human being who is now one (yaaay), walking and chatting and all sorts of stuff, so I’ve been a bit busy. To ease myself back in to all of this, I have decided to share my experience with JaxB (a software framework that allows Java developers to map Java classes to XML representations, according to Wikipedia). I hope it will be helpful.
As an application developer, there’s a high probability that at some point you will have to process XML to achieve some task or another. I recently had to do such a task. Long story short, I had to do stuff, output it into XML, send it to another system that took the XML as input and did some more stuff to it. Make sense? Good. The team that maintains this other system sent me an XSD (schema) that my XML would have to conform to. To those completely new to all of this, in a nutshell, an XML schema has the .xsd extention and it is a little “document” that outlines the rules that your XML should follow, for it to be valid. The XSD tells you which tags are allowed, which are compulsory or optional, what the data types should be used for which tags, etc. For more information about xml and it’s schemas, please visit w3schools. They explain all of it beautifully.
At this point, I am assuming we all have an idea of what XML is and what an XSD is and how it works. So for the point of this exercise, I am going to provide a simple little XSD (from w3schools), and we will use it to learn a little jaxB. The schema is as follows:
<?xml version="1.0" encoding="UTF-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="shiporder"> <xs:complexType> <xs:sequence> <xs:element name="orderperson" type="xs:string"/> <xs:element name="shipto"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="address" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="country" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="item" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element name="note" type="xs:string" minOccurs="0"/> <xs:element name="quantity" type="xs:positiveInteger"/> <xs:element name="price" type="xs:decimal"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="orderid" type="xs:string" use="required"/> </xs:complexType> </xs:element> </xs:schema>
Because this is not an XML tutorial, I am going to assume basic understanding of XML and XSDs. I am not going to go into how you create an XML that can successfully be validated against this schema, I’m just gonna use an online generator to generate a sample XML for this xsd. If anyone would like an actual tutorial on generating an XML from a schema, pop me a comment below and that might be my next post, and no I won’t take an entire year before the next post, I promise. Here’s the XML we’ll be using (basically, we expect our marshalled java output to look like this):
<?xml version="1.0" encoding="utf-8"?> <shiporder orderid="order1234"> <orderperson>John Doe</orderperson> <shipto> <name>John Doe</name> <address>123 Church Street</address> <city>Utopia</city> <country>Outta-this-world</country> </shipto> <item> <title>Egg shipment</title> <note>Fragile</note> <quantity>50</quantity> <price>123.45</price> </item> </shiporder>
Looking at this xml above, I bet you’re thinking: “These are some pretty expensive eggs”. I know. I have a goose that lays golden eggs, in case anyone was wondering. It’s worth a mention as well, for anyone who wasn’t aware, that you can use your IDE to generate xml from an xsd, and a huge chunk of what I’m about to demonstrate can also be generated from your IDE in a matter of seconds if you have the JaxB plugin. But I’m a firm believer that before you generate anything, you need to know how to do it manually. Generate for convenience, not to avoid having to acquire knowledge. You know what they say about knowledge being power and stuff.
I tried to format it properly, I was kind of hoping you can already sort of see the java objects just as you read through the xml. Or at least the JSON representation of it if you had to do it. Your IDE can generate this for you. But for the purpose of this demonstration, I’m gonna do it manually. To represent the xml above, we would end up with 3 java classes, ShipOrder, ShipTo and Item. To map these classes out, we will have to follow the validation rules set out in the xsd. So the classes would look like:
import java.util.List; public class ShipOrder { private String orderPerson; private ShipTo shipTo; private List<Item> items; public ShipOrder(){ } //getters and setters go here }
FYI, the XSD says maxOccurs of the item element is unbounded, which means there can be many items. Hence we have a list of items as opposed to an item.
public class ShipTo { private String name; private String address; private String city; private String country; public ShipTo() { } //getters and setters go here ... }
public class Item { private String title; private String note; private Integer quantity; private Double price; public Item() { } //getters and setters go here ... }
You will notice that the data type of “quantity” is Integer. This means that it would accept a negative value, which it shouldn’t because the xsd clearly says that quantity is a positive Integer. If I had time and energy, and this was super serious, I would write a wrapper class around Integer that limits it’s values to positive ints, but because this is only meant to be a demo of JaxB, let’s not sweat the small stuff. I’m gonna leave it as Integer.
These 3 classes are all just POJOs. I have added a public, no argument constructor in all 3 classes cause it’s a requirement for JaxB. Now we need to add JaxB annotations that will help us map our objects more accurately to XML. For instance, if you look at the ShipTo class above, it has “orderId” as a field just like name, address etc. We might have the illusion that all the fields will be child tags in the shipto tag, when in reality, orderId is an attribute. We need to use annotations to separate the tags from the attributes etc. And we need to also ensure that our tags will be appropriately named. What I mean by that is the following: We have named the class ShipTo, using camel case where the S and T are caps. This is to follow proper coding standards. But we need the xml tag that gets generated from this to have all lower caps cause that’s how it has been set out in the XSD. To sort this out, we will need JaxB annotations. I am going to share what the classes look like after adding the annotations, then I will go through each annotation and what it means after that.
import javax.xml.bind.annotation.*; @XmlRootElement(name = "shiporder") @XmlAccessorType(XmlAccessType.FIELD) public class ShipOrder { @XmlAttribute(name = "orderid") private String orderId; @XmlElement(name = "orderperson") private String orderPerson; @XmlElement(name = "shipto") private ShipTo shipTo; @XmlElement(name = "item") private List<Item> item; public ShipOrder(){ } //getters and setters go here ... }
import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "shipto") @XmlAccessorType(XmlAccessType.FIELD) public class ShipTo { @XmlElement(name = "name") private String name; @XmlElement(name = "address") private String address; @XmlElement(name = "city") private String city; @XmlElement(name = "country") private String country; public ShipTo() { } public ShipTo(String name, String address, String city, String country) { this.name = name; this.address = address; this.city = city; this.country = country; } //getters and setters go here ... }
import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "item") @XmlAccessorType(XmlAccessType.FIELD) public class Item { @XmlElement(name = "title") private String title; @XmlElement(name = "note") private String note; @XmlElement(name = "quantity") private Integer quantity; @XmlElement(name = "price") private Double price; public Item() { } public Item(String title, String note, Integer quantity, Double price){ this.title = title; this.note = note; this.quantity = quantity; this.price = price; } //getters and setters go here ... }
@XmlRootElement: This maps a class or an enum type to an XML root element. When a top-level class or an enum type is annotated with the @XmlRootElement
annotation, then its value is represented as an XML element in an XML document. In the brackets we have a name attribute. The value of this attribute gets set as the name of the XML tag when the XML gets generated. For example, if your class/field was called myTag, but you annotated it with @XmlRootElement/@XmlElement (for class/ field respectively), and you set the name attribute to yourTag, i,e @XmlRootElement(name=”yourTag”), then on the xml document, the tag would appear as yourTag. If you don’t set the name attribute, then it will appear as myTag. I hope this isn’t confusing.
@XmlElement: Maps a field or property to an XML element
@XmlAttribute: Maps a field or property to an XML attribute
To completely break this down to absolute molecular level, the @XmlElement annotation makes your field an xml element, which is the stuff in the angular brackets (<stuff>) and @XmlAttribute annotation makes your field and xml attribute to the root element represented by that class. (<stuff stuffAttribute=”some value”>). I admit, this is a terrible explanation of element and attribute, head on over to w3schools for that part of things my dear friends, my focus is more on how JaxB works.
@XmlAccesorType: Defines the fields and properties of your Java classes that the JAXB engine uses for binding. It has four values: PUBLIC_MEMBER
, FIELD
, PROPERTY
and NONE
. I am not going to get into what these mean. There’s amazing javadocs on these however.
So now that we have appropriately annotated our POJOs, we can move on to writing the method that creates our xml from the POJOs, a process called Marshalling. (Doing the opposite is unmarshalling). This is where the good stuff is. We’re gonna do it with and without the schema validation. And it looks like this:
private static String jaxbObjectToXML(ShipOrder shipOrder) throws JAXBException, SAXException { StringWriter stringWriter = new StringWriter(); URL url; try { JAXBContext jaxbContext = JAXBContext.newInstance(ShipOrder.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); jaxbMarshaller.marshal(shipOrder, stringWriter); return stringWriter.toString(); } catch (JAXBException e) { throw e; //obviously bad code, do better! } }
public static void main(String[] args){ List items = new ArrayList<>(); items.add(new Item("Egg Shipment", "Fragile", 50, 50.00)); items.add(new Item("Parcel 2", "Handle with care", 2, 85.00)); ShipTo shipTo = new ShipTo("John Doe", "100 Grayston", "Johannesburg", "south africa"); String orderId = "order123"; String orderPerson = "John Doe"; String outputXml = jaxbObjectToXML(new ShipOrder(orderId, orderPerson, shipTo, items)); System.out.println(outputXml); }
Running the main method would print the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <shiporder orderid="order123"> <orderperson>John Doe</orderperson> <shipto> <name>John doe</name> <address>100 Grayston</address> <city>Johannesburg</city> <country>south africa</country> </shipto> <item> <title>Egg Shipment</title> <note>Fragile</note> <quantity>50</quantity> <price>50.0</price> </item> <item> <title>Parcel 2</title> <note>Handle with care</note> <quantity>2</quantity> <price>85.0</price> </item> </shiporder>
Pretty freakin’ easy right? Now you don’t have to create your xml from a string builder and do all sorts of freakish gymnastics to manipulate the string to get it to do what you want. You get to work with actual Java objects but still have your output be xml. And remember kids, tests, tests, tests. Write your tests. TDD the crap out of every solution you write then you don’t need to “System.out.print” in the main method to see the output. Write your tests.
So with this solution that we have above, it works and does the job. But it’s not enough. It does absolutely no schema validations. That means that, if we gave input that went against the rules set out in the schema, our xml would still get generated without a problem. That might be okay for some use-cases, but best practice is to respect the xsd, if the validation fails on your end, it’s just gonna go fail with whatever host system you’re sending it to anyway and I’m guessing you don’t want that to happen. Next we’re gonna tackle how to do that, for those who are interested in that sort of thing.
We need to modify the jaxbObjectToXml method as follows:
private static String jaxbObjectToXML(ShipOrder shipOrder) throws JAXBException, SAXException { StringWriter stringWriter = new StringWriter(); URL url; try { JAXBContext jaxbContext = JAXBContext.newInstance(ShipOrder.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); url = Resources.getResource(PATH_TO_XSD); Schema messageSchema = schemaFactory.newSchema(url); jaxbMarshaller.setSchema(messageSchema); jaxbMarshaller.marshal(shipOrder, stringWriter); return stringWriter.toString(); } catch (JAXBException e) { throw e; } catch (SAXException e) { throw e; } }
Now that we have set the schema, when jaxbMarshaller.marshal(shipOrder, stringWriter) is called, a schema validation will be run, and if the xml generated violates any of the “rules” set out in the schema, then an exception will be thrown. This safeguards against invalid input, missing tags that are supposed to be compulsory, repeating tags where only one occurrence is allowed etc. The exception is actually your friend here. It’s what you want, then you know your xml is invalid before sending it anywhere or doing any further processing on it.
Anyway, that’s all folks. I hope you enjoyed this, and that you wrote your tests and dealt with your exceptions properly instead of just rethrowing them. Do as I say, not as I do, okay. And floss. No seriously, dental hygiene is important you guys. Floss, floss, floss, I can not stress this enough 😀
Adios Mi gente!!