Sviatlana Sviatlana - 5 months ago 28
Java Question

How to parse xml using JAXB?

I would like to use JAXB for mapping XML file to object. How to convert XML file like this:

<Contract>
<Variable Name="Address">
<Value>Address</Value>
</Variable>
<Variable Name="FirstName">
<Value>First</Value>
</Variable>
<Variable Name="SecondName">
<Value>Second</Value>
</Variable>
<Parameter Name="id">
<Value>399425</Value>
</Parameter>
<Parameter Name="isComplete">
<Value>True</Value>
</Parameter>
<Parameter Name="templateName">
<Value>Probate Application</Value>
</Parameter>
</Contract>


To java object like this:

public class Contract {
private Long id;
private String templateName;
private boolean isComplete;
private Map<String, String> answers = new HashMap<>();

}


Or maybe advice some tutorial for this.
Thank you

Answer

You have two problems : Binding the <Variable> Element to your Map and Binding the <Parameter> elements to different typed Objects.

I'll treat the problems in this order.

For the Map, you'll have to use a XmlAdapter with @XmlJavaTypeAdapter.

Code a class representing your <Variable> element and map it :

public class Variable{

    @XmlAttribute
    public String Name;

    @XmlElement
    public String Value;

    //Getters and setters
}

Then code the XmlAdapter :

public class YourAdapter extends XmlAdapter<List<Variable>,Map<String,String>>{

    public Map<String,String> unmarshall(List<Variable> arg0){
         //Logic to convert a List<Variable> into a Map<String,String> and return it
    }

    public List<Variable> marshall(Map<String,String> arg0){
         //Logic to convert a Map<String,String> into a List<Variable> and return it
    }


}

Then map your Contract class :

@XmlRootElement
@XmlSeeAlso({Variable.class})
public class Contract{

     @XmlElement(name="Variable")
     @XmlJavaTypeAdapter(YourAdapter.class)
     private Map<String,String> map;

     //Getters and setters
}

With this, your map should work.

You have two solutions with your second problem : Either Using JAXB Reference implementation or Using JAXB MOXy implementation.

With JAXB reference implementation

JAXB Reference implementation cannot use Xpath to do its binding. So here is a workaround.

Code a second class and adapter for your <Parameter> element, which is basically going to be the same thing except for the names, and use this mapping for contract :

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Variable.class,Parameter.class})
public class Contract{

     @XmlElement(name="Variable")
     @XmlJavaTypeAdapter(YourAdapter.class)
     private Map<String,String> map;

     @XmlElement(name="Parameter")
     @XmlJavaTypeAdapter(YourAdapterForParameter.class)
     private Map<String,String> parameterMap;

     public Long getId(){
          return Long.valueOf(parameterMap.get("id"));
     }

     public void setId(Long id){
          parameterMap.put("id",id.toString());
     }

     //Same thing for other getters/setters
}

With MOXy implementation

With MOXy implementation, you can use @XmlPath and XPath to make it a bit cleaner :

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({Variable.class})
public class Contract{

     @XmlPath("./Variable[@Name='id']/Value")
     private Long id;

     @XmlPath("./Variable[@Name='templateName']/Value")
     private String templateName;

     @XmlPath("./Variable[@Name='isComplete']/Value")
     private boolean isComplete;

     @XmlElement(name="Variable")
     @XmlJavaTypeAdapter(YourAdapter.class)
     private Map<String,String> map;

     //Getters and setters
}

For further information/help, please refer to JAXB/MOXy Documentation.