Search code examples
javaxmljaxb2

how can i construct the xml element based on the type attribute when using jaxb2?


I'm using java jaxb2.0 to do xml marshalling and unmarshalling.
I encountered a problem like this.
the sample xml is like this*(fruit.xml)*:

<fruitPacks>
  <fruit name="apple1" type="apple" isApple="true"/>
  <fruit name="banana1" type="banana" isBanana="true"/>
  <fruit name="apple2" type="apple" isApple="true"/>
</fruitPacks>

and java class like this:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "fruitPacks")
@XmlRootElement
public class FruitPacks
{
   @XmlElement(name = "fruit")
   private List<Fruit> fruits
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "fruit")
@XmlSeeAlso({ Apple.class, Banana.class })
public class Fruit{
 @XmlAttribute
 private String name;
 @XmlAttribute
 private String type;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "apple")
public class Apple extends Fruit{
  @XmlAttribute
  private boolean isApple = true;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "banana")
public class Banana extends Fruit{
  @XmlAttribute
  private boolean isBanana = true;
}

and unmarshal code is :

public class JAXBTest {

public static void main(String [] args) throws Exception{
    JAXBContext jc = JAXBContext.newInstance(FruitPacks.class,Fruit.class,Apple.class, Banana.class);

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    Object obj = unmarshaller.unmarshal(new File("fruit.xml"));
    System.out.println(obj);
}   
}

So what I want to do is:
when unmarshalling the xml, the construction of the fruit will automatically done to the subclasses (Apple, Banana) based on the type attribute.
How can i do it?


Solution

  • You need to ensure that your JAXBContext is aware of the subclasses. One way to do this is to use the @XmlSeeAlso annotation, see an example below:

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "fruit")
    @XmlSeeAlso({Apple.class, Banana.class})
    public class Fruit{
     @XmlAttribute
     private String name;
     @XmlAttribute
     private String type;
    }
    

    For More Information


    UPDATE

    I just realized you are using the type attribute and not the standard xsi:type attribute. With any JAXB implementation you could map this use case leveraging an XmlAdapter:

    If you are using EclipseLink JAXB (MOXy) then you can leverage the @XmlDescriminatorNode/@XmlDescriminatorValue extension: