问题
I'm trying to create JAXB annotated classes to generate XML based on the Microsoft SharePoint Query schema. I have an SpWhereClause
class:
@XmlType(name="Where")
@XmlAccessorType(XmlAccessType.FIELD)
public class SpWhereClause {
}
But I'm not sure how to structure/annotate its properties. A <Where>
element can have many different types of child elements (<Eq>
, <BeginsWith>
, <Contains>
etc. Let's ignore <And>
and <Or>
for now), but not more than one. Eq
and BeginsWith
are each individually valid children of Where
, but it can't be like this:
<Where>
<Eq>...</Eq>
<BeginsWith>...</BeginsWith>
</Where>
without nesting the <Eq>
and <BeginsWith>
in an <Or>
element.
My first thought was to create an AbstractSpComparison
class with the <FieldRef>
and <Value>
elements common to all comparisons:
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractSpComparison {
@XmlElement
private SpFieldRef fieldRef;
@XmlElement
private SpValue value;
...
}
then have SpEqualsComparison
extend it:
@XmlType(name="Eq")
public class SpEqualsComparison extends AbstractSpComparison {
}
However, using the abstract class in SpWhereClause
leaves me unable to control the name of the child element. This:
@XmlElement
private AbstractSpComparison comparison;
Results in this:
<Where>
<comparison>...</comparison>
</Where>
instead of this:
<WHERE>
<Eq>...</Eq>
</WHERE>
Why isn't the "Eq" name from SpEqualsComparison
being used to name the comparison element? What's the right way to handle a situation like that?
I considered just having every possible child element type as a property of SpWhereClause
, and only set the one I need (with some validation logic somewhere), but that seems unnecessarily verbose.
If it matters, I'm using a Spring OXM Jaxb2Marshaller
.
回答1:
There are a couple of different options you could do:
Option #1 - @XmlElementRef
If everything extends a common super class (i.e. AbstractSpComparison) then you could leverage the @XmlElementRef
annotation. When you do this the child element will correspond to the value of the @XmlRootElement
annotation on the class of the referenced object.
@XmlElementRef
private AbstractSpComparison comparison;
For More Information
I have written more about this approach on my blog:
- http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
Option #2 - @XmlElements
If everything doesn't extend a common super class then you can leverage the @XmlElements
annotation where you explicitly annotate which elements could appear and what class they correspond to:
@XmlElements({
@XmlElement(name="Eq", type=Eq.class),
@XmlElement(name="BeginsWith", type=BeginsWith.class)
})
private AbstractSpComparison comparison;
For More Information
I have written more about this approach on my blog:
- http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html
来源:https://stackoverflow.com/questions/24168673/using-jaxb-to-marshall-an-xml-node-that-can-contain-only-one-of-multiple-child-n