I have a class Person with attributes name and address. I display it in a XML. While unmarshalling from XML will it be possible to get line number for name and address separately. I tried using Locator. But it does not provide individual line numbers.
Asked
Active
Viewed 2,606 times
2 Answers
3
The EclipseLink JAXB (MOXy) and the JAXB reference implementation each have their own @XmlLocation
annotations for supporting this use case. This allows you to store the location on the XML element corresponding to the object as an instance of org.xml.sax.Locator
. Since I'm the MOXy lead, I will demonstrate using MOXy:
Person
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
@XmlJavaTypeAdapter(value=StringAdapter.class)
String name;
Address address;
@XmlLocation
Locator locator;
}
Address
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class Address {
@XmlJavaTypeAdapter(value=StringAdapter.class)
private String street;
@XmlLocation
Locator locator;
}
StringAdapter
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String> {
public static class AdaptedString {
@XmlValue
public String value;
@XmlLocation
@XmlTransient
Locator locator;
}
@Override
public String unmarshal(AdaptedString v) throws Exception {
System.out.println(v.value + " " + v.locator.getLineNumber());
return v.value;
}
@Override
public AdaptedString marshal(String v) throws Exception {
AdaptedString adaptedString = new AdaptedString();
adaptedString.value = v;
return adaptedString;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties
in the same package as your domain model with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14455596/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
System.out.println("Person: " + person.locator.getLineNumber());
System.out.println("Address: " + person.address.locator.getLineNumber());
}
}
Output
Jane Doe 3
1 A Street 5
Person: 2
Address: 4

bdoughan
- 147,609
- 23
- 300
- 400
-
In the above example, will it be possible to get the line number for Street which is a String value in XML file – Anand B Jan 22 '13 at 11:14
-
@AnandB - Where do you want to store the line number for `Street`? Also out of curiosity why do you want to store all the line numbers? – bdoughan Jan 22 '13 at 11:15
-
@AnandB - I have updated my answer with an `XmlAdapter` approach that may work. Ultimately you may not want to use `String` properties and just use a class like `AdaptedString` directly in your model. – bdoughan Jan 22 '13 at 11:31
-
@AnandB - It has now been added. – bdoughan Jan 22 '13 at 11:44
-
1@BlaiseDoughan: Is there a way to have the classes auto-generated from schema? – Rekin Nov 21 '14 at 08:32
2
You could leverage a StAX StreamReaderDelegate
and do something like the following:
Demo
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource source = new StreamSource("src/forum14455596/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(source);
xsr = new StreamReaderDelegate(xsr) {
@Override
public String getLocalName() {
String localName = super.getLocalName();
if(isStartElement()) {
System.out.println(localName + " " + this.getLocation().getLineNumber());
}
return localName;
}
};
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(xsr);
}
}
Person
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String name;
private String address;
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>Jane Doe</name>
<address>1 A Street</address>
</person>
Output
person 2
name 3
address 4

bdoughan
- 147,609
- 23
- 300
- 400
-
How will I be able to achieve the same in case of nested XML files. Will it be possible to have a property like Locator inside individual classes. – Anand B Jan 22 '13 at 10:34
-
@AnandB - That code will output the line number for each start element regardless of how deep the XML is. – bdoughan Jan 22 '13 at 10:35
-
Will it be possible to have a property like Locator inside individual classes. – Anand B Jan 22 '13 at 10:36
-
@AnandB - You just want to store the location for each domain object? – bdoughan Jan 22 '13 at 10:38
-
@AnandB - I have added another answer demonstrating how to do this with the `@XmlLocation` extension. – bdoughan Jan 22 '13 at 10:56