Using JAXB to marshall an XML node that can contain only one of multiple child node types (SharePoint Query)

偶尔善良 提交于 2019-12-11 00:54:43

问题


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

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