JAXB override @XmlElement type of list
Asked Answered
A

1

2

There's a simple class Bean1 with a sublist of type BeanChild1.

@XmlRootElement(name="bean")
@XmlAccessorType(XmlAccessType.PROPERTY)
public static class Bean1
{
  public Bean1()
  {
    super();
  }

  private List<BeanChild1> childList = new ArrayList<>();

  @XmlElement(name="child")
  public List<BeanChild1> getChildList()
  {
    return childList;
  }

  public void setChildList(List<BeanChild1> pChildList)
  {
    childList = pChildList;
  }
}

public static class BeanChild1 { ... }

I am trying to override the class, to change the type of the list. The new child-class (i.e. BeanChild2) extends the previous one (i.e. BeanChild1) .

public static class Bean2 extends Bean1
{
  public Bean2()
  {
    super();
  }

  @Override
  @XmlElement(name="child", type=BeanChild2.class)
  public List<BeanChild1> getChildList()
  {
    return super.getChildList();
  }
}

public static class BeanChild2 extends BeanChild1 { }

So, here is how I tested it:

public static void main(String[] args)
{
  String xml = "<bean>" +
               "  <child></child>" +
               "  <child></child>" +
               "  <child></child>" +
               "</bean>";
  Reader reader = new StringReader(xml);

  Bean2 b2 =  JAXB.unmarshal(reader, Bean2.class);
  assert b2.getChildList().get(0) instanceof BeanChild2; // fails
}

The test reveals that that this list still contains childs of BeanChild1.

So, how can I force it to populate the childList field with BeanChild2 instances ?

If there are no easy solutions, then feel free to post more creative solutions (e.g. using XmlAdapters, Unmarshaller.Listener, perhaps an additional annotation on the parent or child class ...)

Apodal answered 13/7, 2016 at 9:44 Comment(0)
A
0

There is no way to change (e.g. override) the @XmlElement annotation of a super class. At least not using annotations.

  • It doesn't matter what @XmlAccessorType you use (e.g. FIELD, PROPERTY, PUBLIC, NONE).
  • It doesn't make any difference if you put the annotations on the fields or on the getters.

However, there is a reasonable alternative. The MOXy implementation of JAXB offers the ability to define the metadata/bindings in an xml file. In fact every java annotation has an XML alternative. But it gets better: You can combine both java annotations AND these xml metadata. The cool thing, is that MOXy will merge both declarations, and in case of conflict, the XML defined metadata gets a higher priority.

Assuming that the Bean1 class is annotated as above. Then it's possible to redefine the binding, in an xml file. e.g.:

<xml-bindings xml-accessor-type="PROPERTY">
  <java-types>
    <java-type name="Bean1">
      <xml-element java-attribute="childList" name="child" 
                   type="BeanChild2" container-type="java.util.ArrayList" />
    </java-type>
  </java-types>
</xml-bindings>

This new bindings file is needed during the creation of the context object.

// use a map to reference the xml file
Map<String, Object> propertyMap = new HashMap<>();
propertyMap.put(JAXBContextProperties.OXM_METADATA_SOURCE, "bindings.xml");

// pass this properyMap during the creation of the JAXB context.
JAXBContext context = JAXBContext.newInstance(..., propertyMap);

MOXy will merge the java annotations and the XML bindings, and in case of a conflict the XML defined settings are applied. In this case, the earlier @XmlElement(name=child) annotation is replaced by an xml definition which is equivalent to @XmlElement(name=child, type=BeanChild2.class).

You can read more about the XML bindings here.

Apodal answered 27/7, 2016 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.