Ephenodrom
Ephenodrom

Reputation: 1893

JAXB mapping 1 XML Tag to 2 variables

i am trying to use one class to map the response i get from an XML request. But the xml response differs, depending own some settings. For example in a response i get the tag "owner" which is filled with the ID of the owner object. If i add a setting in my request i will get back the full owner data, like the firstname and lastname. Now i want to map the owner tag to either a String variable or a Class depending on the response.

Example :

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "domain")
public class Response {

    @XmlElement
    private String name;

    @XmlElement(name = "owner")
    private String ownerSimple;

    @XmlElement(name = "owner")
    private Owner ownerComplex;

}

@XmlRootElement(name = "ownerc")
public class OwnerC {

    @XmlElement
    int id;

    @XmlElement
    String fname;

    @XmlElement
    String lname;
}

XML to map :

<response>
    <name>Foo</name>
    <owner>1234</owner>  <!-- in this case it's only a id -->
</response>

<response>
    <name>Foo</name>
    <owner>  <!-- in this case it's the owner class -->
        <id>1234</id>
        <fname>Jon</fname>
        <lname>Doe</lname>
    </owner>
</response>

Upvotes: 4

Views: 1341

Answers (1)

pedrofb
pedrofb

Reputation: 39241

You can use @XmlAnyElement(lax=true) to handle this use case. This annotation allows you to unmarshall any XML to a Java object (DOM Node). In a second step, it is possible to unmarshall the Node to the required Object

Response

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "domain")
public class Response {

    @XmlElement
    private String name;

    @XmlAnyElement(lax=true)
    private Object owner;

    private String ownerSimple;

    @XmlTransient
    private Owner ownerComplex; 

Owner

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "owner")
public class Owner {

    @XmlElement
    int id;

    @XmlElement
    String fname;

    @XmlElement
    String lname;

Unmarshaller

//Unmarshaller. Step 1 - Decodes Response and set a DOM Node at Owner
//Important. Owner class must not be present in JAXB context, letting next step to decode the object properly. 
//Owner variable at Response class is annotated with @XmlTransient
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Response response = (Response) jaxbUnmarshaller.unmarshal(reader);

//Unmarshaller. Step 2. Convert Node to the suitable Object
//Considering both cases, simple-> String complex -> Owner Object.      
String ownerSimple = ((Node)response.getOwner()).getFirstChild().getNodeValue();
if (ownerSimple != null){
    response.setOwnerSimple(ownerSimple);
} else {
    JAXBContext jaxbContextOwner = JAXBContext.newInstance(Owner.class);
    Unmarshaller  jaxbUnmarshallerOwner = jaxbContextOwner.createUnmarshaller();
    Owner ownerComplex = (Owner) jaxbUnmarshallerOwner.unmarshal((Node)response.getOwner());
    response.setOwnerComplex(ownerComplex);
}

 //Marshaller to system.out. Your object is well mapped in both cases
 Marshaller marshaller = jaxbContext.createMarshaller();
 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 marshaller.marshal(rx, System.out);

Upvotes: 1

Related Questions