MOXy/JAXB “prototype pattern” - interface inheritance

ぃ、小莉子 提交于 2019-12-11 10:58:48

问题


I'm trying to implement a version of Gang of Four's Prototype Pattern using MOXy/JAXB 2.5.0. I want to be able to specify a list of items, some of which are "based on" others, i.e. copy their data from other instances. For reusability, I'd like to create an interface that any prototype-able object should implement, which will provide annotation for the properties necessary to support the pattern.

@XmlRootElement(name="IPrototype")
public interface IPrototype
{
    /**
     * Acts as a "copy constructor"
     */
    @XmlAttribute(name="prototype")
    @XmlIDREF
    public void setPrototype(IPrototype prototype); 

    @XmlAttribute(name="id")
    @XmlID
    public void setId(String id);

    public String getId();
}

An implementing object would ideally look something like this, not even having to bother with annotation of the methods implemented from the interface:

@XmlRootElement(name="Item")
public class Item implements IPrototype
{
    private String m_id = null;

    private String m_data = null;

    public Item()
    {

    }

    /**
     * Never called
     */
    @Override
    public void setPrototype(IPrototype prototype)
    {
        m_data = ((Item)prototype).getData();
    }

    @Override
    public void setId(String id)
    {
        m_id = id;
    }

    @Override
    public String getId()
    {
        return m_id;
    }

    @XmlAttribute(name="data")
    public void setData(String data)
    {
        m_data = data;
    }

    public String getData()
    {
        return m_data;
    }
}

And the XML would look like:

<Wrapper>
    <Item id="Item1" data="stuff and things" />
    <Item id="Item2" prototype="Item1" />
</Wrapper>

where Wrapper is defined as:

@XmlRootElement(name="Wrapper")
public class Wrapper
{
    @XmlElementRef
    private ArrayList<Item> m_items = null; 
}

If it worked the way I want, I would get a list with two elements, both of type Item, that contain the same data. However, MOXy doesn't seem to "see" the annotations on the interface, and I get a list with two Items that don't have their XmlID set, and setPrototype() is never called. The only solution appears to be annotating the setPrototype() and setId() methods in the Item class, but this appears to require changing setPrototype()'s argument type from IPrototype to Item so MOXy will look for the XmlID in the right class. Unfortunately, this breaks the inherited interface.

If I instead change the list type to IPrototype, hoping that would allow MOXy to see its annotations, I get the same behavior - null ID's, setPrototype() never called. This isn't really what I want anyways, I'd like to be able to constrain which subtypes may be included in a particular list.

Not sure I have correct expectations for how interface annotations are supposed to work, maybe that's the source of my error.

Any thoughts on how to get this working? Thanks in advance,

Steve

UPDATE: If I annotate the Item class, WITHOUT changing setPrototype's method signature (which I guess I can live with), I get a list with two items, with correctly set XmlID's, but setPrototype() is still not called. Seems like MOXy is looking for instances of IPrototype (impossible) with the same XmlID, not Item instances.

UPDATE 2: And, if I convert IPrototype into an abstract class, everything works perfectly. However, given Java's single inheritance model, this is too limiting for a framework object that is intended to supplement multiple inheritance hierarchies. So still stuck.


回答1:


I got a somewhat acceptable version of this running by annotating Item's properties and declaring setPrototype() using generics. This is what the declaration looks like in IPrototype:

public <T> void setPrototype(T prototype);

And here's its implementation in Item:

@XmlAttribute(name="prototype")
@XmlIDREF
@Override
public <T> void setPrototype(T prototype)
{
    m_data = ((Item)prototype).getData();
}

It works, but I don't particularly like having to re-annotate the subclass. I've opened a separate question about that, since it doesn't intuitively (to me) seem like that's the way interface annotations should work:

MOXy/JAXB interface annotation




回答2:


You can use MOXy's external binding document to override the super class of the implementing clases (i.e. Item) from Object to the interface IPrototype. This will allow you to inherit the mappings from the interface.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum16807019">
    <java-types>
        <java-type name="Item" super-type="forum16807019.IPrototype"/>
    </java-types>
</xml-bindings>

For a complete example see:

  • MOXy/JAXB interface annotation


来源:https://stackoverflow.com/questions/16807019/moxy-jaxb-prototype-pattern-interface-inheritance

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