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 XmlAdapter
s, Unmarshaller.Listener
, perhaps an additional annotation on the parent or child class ...)
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