Search code examples
jaxbjaxb2

Line number of individual XML element while unmarshalling using jaxb


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.


Solution

  • 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