JAXB override @XmlElement type of list

限于喜欢 提交于 2020-01-02 14:04:58

问题


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 ...)


回答1:


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.



来源:https://stackoverflow.com/questions/38348083/jaxb-override-xmlelement-type-of-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!