Chris
Chris

Reputation: 449

Using JAXB, how can I unmarshal into a java object with multiple XmlJavaTypeAdapter references?

I'm trying to use JAXB to unmarshal a data (Polygon) with two @XmlJavaTypeAdapter annotations (Subscriber, Tag) to and from an XML file. Each object has it's own XML file, and the objects are related thus:

I'm trying to unmarshall the Polygon XML file back to its class object, with its Subscribers and Tags in tact, but can't work out how to do it.

== Subscriber.java ==

@XmlRootElement(name = "Subscriber")
@XmlAccessorType(XmlAccessType.FIELD)
public class Subscriber {
    private String ID;
    private Set<Polygon> polygons;

    @XmlAttribute(name = "ID")
    public String getID() {
        return this.ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    @XmlJavaAdapter(PolygonXmlAdapter.class)
    @XmlElementWrapper(name = "Polygons")
    @XmlElement(name = "Polygon")
    public set<Polygon> getPolygons() {
        return this.polygons;
    }

    public void setPolygons(Set<Polygon> polygons) {
        this.polygons = polygons;
    }
}

== Polygon.java ==

@XmlRootElement(name = "{Polygon}")
@XmlAccessorType(XmlAccessType.FIELD)
public class Polygon {
    private String ID;
    private Set<Subscriber> subscribers;
    private Set<Tag> tags

    @XmlAttribute(name = "ID")
    public String getID() {
        return this.ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    @XmlJavaAdapter(SubscriberXmlAdapter.class)
    @XmlElementWrapper(name = "Subscribers")
    @XmlElement(Subscriber)
    public Set<Subscriber> getSubscribers() {
        return this.subscribers;
    }

    @XmlJavaAdapter(TagXmlAdapter.class)
    @XmlElementWrapper(name = "Tags")
    @XmlElement(name = "Tag")
    public Set<Tag> getTags() {
        return this.tags
    }

    public void setTags(Set<Tag> tags) {
        this.tags = tags;
    }
}

== Tag.java ==

@XmlRootElement(name = "Tag")
@XmlAccessorType(XmlAccessType.FIELD)
public class Tag {
    private String ID;

    @XmlAttribute(name = "ID")
    public String getID() {
        return this.ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }
}

== SubscriberXmlAdapter.java ==

public class SubscriberXmlAdapter extends XmlAdapter<String, Subscriber> {
    private Map<String, Subscriber> subscribers = new HashMap<>();

    private Map<String, Subscriber> getSubscribers() {
        return subscribers;
    }

    @Override
    public Subscriber unmarshal(String v) throws Exception {
        return subscribers.get(v);
    }

    @Override
    public String marshal(Subscriber v) throws Exception {
        return v.getID();
    }
}

== TagXmlAdapter.java ==

public class TagXmlAdapter extends XmlAdapter<String, Tag> {
    private Map<String, Tag> tags = new HashMap<>();

    private Map<String, Tag> getTags() {
        return tags;
    }

    @Override
    public String unmarshal(String v) throws Exception {
        return tags.get(v);
    }

    @Override
    public String marshal(Tag v) throws Exception {
        reutrn v.getID();
    }
}

== SubscriberCollection.java ==

@XmlRootElement(name = "Subscribers")
@XmlAccessorType(XmlAccessType.FIELD)
public class SubscriberCollection {
    private Collection<Subscriber> collection;

    @XmlElement(name = "Subscriber")
    public Collection<Subscriber> getSubscribers() {
        return this.collection;
    }

    public void setCollection(Collection<Subscriber> collection) {
        this.collection = collection;
    }
}

== TagCollection.java ==

@XmlRootElement(name = "Tags")
@XmlAccessorType(XmlAccessType.FIELD)
public class TagCollection {
    private Collection<Subscriber> collection;

    @XmlElement(name="Tag")
    public Collection<Tag> getSubscribers() {
        return this.collection;
    }

    public void setCollection(Collection<Subscriber> collection) {
        this.collection = collection;
    }

}

And to execute the unmarshalling, three xml files - for subscribers, polygons, and tags - containing their respective data are used:

JAXBContext jaxbContext = JAXBContext.newInstance(SubscriberCollection.class, PolygonTagCollection.class, Polygon.class);

unmarshaller subUnmarshaller = jaxbContext.createunmarshaller();
SubscriberCollection subCollection = (SubscriberCollection) subUnmarshaller.unmarshal(new ByteArrayInputStream(subscribersXml.getBytes(StandardCharset.UTF_8.name())));

Unmarshaller tagUnmarshaller = jaxbContext.createUnmarshaller();
TagCollection tagCollectino = (TagCollection) tagUnmarshaller.un,arshal(new ByteArrayInputStream(tagXml.getBytes(StandardCharsets.UTF_8.name())));

Unmarshaller polUnmarshaller = jaxbContext.createUnmarshaller();

SubscriberXmlAdapter subAdapter = new SubscriberXmlAdapter();
for (Subscriber sub : subscriberCollection.getCollection()) {
    subAdapter.getCollection().put(sub.getID(), sub);
}

TagXmlAdapter tagAdapter = new TagXmlAdapter();
for (Tag tag : tagCollection.getCollection()) {
    tagAdapter.getCollection().put(tag.getID(), tag);
}

polUnmarshaller.setAdapter(SubscriberXmlAdapter.class subAdapter);

polUnmarshaller.unmarshal(new ByteArrayInputStream(polygonXml.getBytes(StandardCharsets.UTF_8.name())));    

This unmarshalls the Polygon with the correct subscribers, but not with the correct tags.

I can only set one XmlAdapter to the unmarshaller, so it can only populate the subscribers or the tags within the polygon being unmarshalled.

Is there a way to combine these adapters? Or is there a better way of doing things altogether.

Thanks!

Upvotes: 0

Views: 427

Answers (0)

Related Questions