简体   繁体   中英

JAXB Unmarshal subclass based in child element value

I need to mapper the follow xml based in the value of element type. The BaseEntity superclass have the common elements to the Machine and Robot classes. Each subclasse have different elements... The XML structure is fixed and I can't change it. Each entry element should be mapped to the respective class, entry/type=Machine should be mapped to subclass Machine and so on...

It is possible in JAXB? How can I implement this? Any suggestion?

<root>
    <entries>
        <entry>
            <name>RTM</name>
            <description>RealTime Machine</description>
            <code>RTM1</code>
            <type>Machine</type>
        </entry>
        <entry>
            <name>RTM</name>
            <description>RealTime Machine</description>
            <type>Robot</type>
            <serial>RS123<serial>
        </entry>
    </entries>
</root>

public abstract class BaseEntity {
    private String name;
    private String description;
}
public class Machine extends BaseEntity{
    private String code;
}
public class Robot extends BaseEntity{
    private String serial;
}

Unfortunately your XML layout is really bad. When the JAXB parser enters an entry node, it can't tell by the node whether it is a machine oder robot. So it must guess which type to use for unmarshlling. Normally you would use the type as node name (1) or provide an attribute with the type parameter (2).

1:

<entries>
    <machine>...</machine>
    <robot>...</robot>
</entries>

2:

<entries>
    <entry type="machine">...</entry>
    <entry type="robot">...</entry>
</entries>

So my solution to your problem is as frankenstein as your XML.

Have an adapted class, which extends the base class and has all the attributes of the available subclasses:

@XmlRootElement(name = "ENTRY")
@XmlAccessorType(XmlAccessType.FIELD)
public class AdaptedBaseEntry extends BaseEntry
{


    @XmlElement(name = "CODE", required = false)
    public String code;

    @XmlElement(name = "SERIAL", required = false)
    public String serial;

    public AdaptedBaseEntry()
    {
        super();
    }

    public AdaptedBaseEntry(BaseEntry entry)
    {
        super(entry.type, entry.description, entry.name);
        if (entry instanceof Machine)
        {
            this.code = ((Machine) entry).code;
        } else if (entry instanceof Robot)
        {
            this.serial = ((Robot) entry).serial;
        }
    }

}

Use an XmlAdapter to bind the adapter class to XML and vice versa:

public class BaseEntryAdapter extends XmlAdapter<AdaptedBaseEntry, BaseEntry>
{

    @Override
    public BaseEntry unmarshal(AdaptedBaseEntry v) throws Exception
    {
        switch (v.type)
        {
        case Machine:
            return new Machine(v.name, v.description, v.code);
        case Robot:
            return new Robot(v.name, v.description, v.serial);
        default:
            return null;
        }
    }

    @Override
    public AdaptedBaseEntry marshal(BaseEntry v) throws Exception
    {
        return new AdaptedBaseEntry(v);
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM