Recently I came across a question on Stack Overflow asking how find where in the document an object was unmarshalled from. In this post I'll expand on my answer of using an XMLStreamReader and Unmarshaller.Listener to accomplish this.
LocationListener
package blog.unmarshaller.listener; import java.util.HashMap; import java.util.Map; import javax.xml.bind.Unmarshaller.Listener; import javax.xml.stream.Location; import javax.xml.stream.XMLStreamReader; class LocationListener extends Listener { private XMLStreamReader xsr; private Map<Object, Location> locations; public LocationListener(XMLStreamReader xsr) { this.xsr = xsr; this.locations = new HashMap<Object, Location>(); } @Override public void beforeUnmarshal(Object target, Object parent) { locations.put(target, xsr.getLocation()); } public Location getLocation(Object o) { return locations.get(o); } }
Demo
The LocationListener holds a reference to the XMLStreamReader we are unmarshalling from. We set in on the Unmarshaller so that we can receive events during the unmarshal operation. The LocationListener maintains a map of Object to Location instances, that we can use to look up the locations of the unmarshalled objects.
package blog.unmarshaller.listener; import java.io.FileInputStream; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Customer.class); XMLInputFactory xif = XMLInputFactory.newFactory(); FileInputStream xml = new FileInputStream("src/blog/unmarshaller/listener/input.xml"); XMLStreamReader xsr = xif.createXMLStreamReader(xml); Unmarshaller unmarshaller = jc.createUnmarshaller(); LocationListener ll = new LocationListener(xsr); unmarshaller.setListener(ll); Customer customer = (Customer) unmarshaller.unmarshal(xsr); System.out.println(ll.getLocation(customer)); System.out.println(ll.getLocation(customer.getAddress())); } }
input.xml
Below is the XML that will be unmarshalled:
<?xml version="1.0" encoding="UTF-8"?> <customer> <name>Jane Doe</name> <address> <street>123 A Street</street> </address> </customer>
Output
Below is the output from running the example:
[row,col {unknown-source}]: [2,1] [row,col {unknown-source}]: [4,5]
Java Model
The following domain model was used for this example:
Customer
package blog.unmarshaller.listener; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Customer { private String name; private Address address; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
Address
package blog.unmarshaller.listener; public class Address { private String street; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } }
Further Reading
For more information on JAXB events refer to the EclipseLink JAXB (MOXy) User Guide:
Thank you for the great explenation and demo code it helps me a lot. But I have one problem how I get the line number of the name or street element? I did a lot of tests but I don't find a solution.
ReplyDelete