Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't Marshal java.lang.String

Here is my dilemma:

I have a dto class for marshaling back and forth from/to XML.

Here is the trick: Because of the number of dto classes our project deals with that are collections with a plural outter tag, I decided to create a delegate collection that allows me to take one of these classes and effortlessly turn them into a Collection and get the convenience that comes with it (iteration, add, etc.).

In our project we have marshaling tests to flush out annotation errors and such. Below is my trouble code.

Problem: Depending on the marshaler, if I extend this QuickCollection I get the below error. When the object is unmarshaled to xml using CXF as a response to a webservice request, it fails. Exact error: com.sun.istack.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation

When it's marshaled/unmarshaled with JAXB in test it's fine. When This same QuickCollection is used to marshal in results from 3rd parties using spring RestOperations and works fine

the mind screw: When I remove the inheritance and manage the collection as a private member it all just works!

This makes not a stitch of sense to me as I am literally returning the exact data type in both situations.

Below is all relevant code.

This is the Inherited delegate class.

    public class QuickCollection<T> implements Collection<T> {
    // to be set if needed after instantiation. To behave like a normal collection, we set it to something safe
    protected Collection<T> delegate = Collections.emptySet();

    public QuickCollection() {
    }

    public QuickCollection(Collection<T> delegate) {
        this.delegate = delegate;
    }

    @Override
    public int size() {
        return delegate.size();
    }

    @Override
    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return delegate.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
        return delegate.iterator();
    }

    @Override
    public Object[] toArray() {
        return delegate.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return delegate.toArray(a);
    }

    @Override
    public boolean add(T t) {
        return delegate.add(t);
    }

    @Override
    public boolean remove(Object o) {
        return delegate.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return delegate.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        return delegate.addAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return delegate.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return delegate.retainAll(c);
    }

    @Override
    public void clear() {
        delegate.clear();
    }

    @Override
    public String toString() {
        return "" + delegate.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        QuickCollection that = (QuickCollection) o;

        if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return delegate != null ? delegate.hashCode() : 0;
    }
}

Here is the child DTO class

@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "BuddyCodes")
@XmlRootElement(name = "BuddyCodes")
public class BuddyCodes extends QuickCollection<String> implements Xml {

    private Long accountId;

    private Date expirationDate;

    public BuddyCodes() {
        super.delegate = new HashSet<String>();
    }

    public BuddyCodes(Long accountId, Set<String> codes, Date expirationDate) {
        super(codes);
        this.accountId = accountId;
        this.expirationDate = expirationDate;
        super.delegate = new HashSet<String>();

    }

    public BuddyCodes(Long accountId, Date expirationDate) {
        this.accountId = accountId;
        this.expirationDate = expirationDate;
        super.delegate = new HashSet<String>();
    }

    @Override
    public String toXml() {
        String retVal;
        try {
            retVal = StringUtils.toXml(this);
        }
        catch (JAXBException e) {
            retVal = e.toString();
        }
        return retVal;

    }

    public Long getAccountId() {
        return accountId;
    }

    public void setAccountId(Long accountId) {
        this.accountId = accountId;
    }

    public Set<String> getCodes() {
        return (Set<String>) super.delegate;
    }

    @XmlElement(name = "code")
    public void setCodes(Set<String> codes) {
        super.delegate = codes;
    }

    public Date getExpirationDate() {
        return expirationDate;
    }

    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        BuddyCodes that = (BuddyCodes) o;

        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false;
        if (delegate != null ? !super.delegate.equals(that.delegate) : that.delegate != null) return false;
        if (expirationDate != null ? !expirationDate.equals(that.expirationDate) : that.expirationDate != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = accountId != null ? accountId.hashCode() : 0;
        result = 31 * result + (expirationDate != null ? expirationDate.hashCode() : 0);
        result = 31 * result + (super.delegate != null ? super.delegate.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "BuddyCodes{" +
                "accountId=" + accountId +
                "codes=" + super.delegate +
                ", expirationDate=" + expirationDate +
                '}';
    }
}

And it doesn't work. I get the error.

Now, here is the child class after removing the inheritance and it works!!!

import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.*;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * @author christian.bongiorno
 *         Date: 10/3/11
 *         Time: 6:11 PM
 */
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name = "BuddyCodes")
@XmlRootElement(name = "BuddyCodes")
public class BuddyCodes implements Xml {

    private Long accountId;

    private Date expirationDate;
    private Set<String> delegate;
    public BuddyCodes() {
        delegate = new HashSet<String>();
    }

    public BuddyCodes(Long accountId, Set<String> codes, Date expirationDate) {
        this.accountId = accountId;
        this.expirationDate = expirationDate;
        delegate = new HashSet<String>();

    }

    public BuddyCodes(Long accountId, Date expirationDate) {
        this.accountId = accountId;
        this.expirationDate = expirationDate;
        delegate = new HashSet<String>();
    }

    @Override
    public String toXml() {
        String retVal;
        try {
            retVal = StringUtils.toXml(this);
        }
        catch (JAXBException e) {
            retVal = e.toString();
        }
        return retVal;

    }

    public Long getAccountId() {
        return accountId;
    }

    public void setAccountId(Long accountId) {
        this.accountId = accountId;
    }

    public Set<String> getCodes() {
        return delegate;
    }

    @XmlElement(name = "code")
    public void setCodes(Set<String> codes) {
        delegate = codes;
    }

    public Date getExpirationDate() {
        return expirationDate;
    }

    public void setExpirationDate(Date expirationDate) {
        this.expirationDate = expirationDate;
    }

    public boolean add(String s) {
        return delegate.add(s);
    }

    public int size() {
        return delegate.size();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        BuddyCodes that = (BuddyCodes) o;

        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) return false;
        if (delegate != null ? !delegate.equals(that.delegate) : that.delegate != null) return false;
        if (expirationDate != null ? !expirationDate.equals(that.expirationDate) : that.expirationDate != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = accountId != null ? accountId.hashCode() : 0;
        result = 31 * result + (expirationDate != null ? expirationDate.hashCode() : 0);
        result = 31 * result + (delegate != null ? delegate.hashCode() : 0);
        return result;
    }


}

Why does the inheritance matter at all???

I haven't figured this out but, I have another DTO in a similar layout (BuddyTypes BuddyType). BuddyType has 2 members: Long and String. Both are annoted as XmlElement. This one works just fine.

It seems the problem that the members of the set making up the delegate are not annotated in my problem case and I don't know how to annotate a parent member. As an inherited class, it wouldn't make sense to have some sort of default name/annotation. But, I tried this madness and the annotation is ignored -- I have seen parent member annotations ignored before so this isn't new.

I don't know if it's possible, but I need to annotate a parent member.

like image 259
Christian Bongiorno Avatar asked Oct 07 '11 06:10

Christian Bongiorno


1 Answers

A bit out of the box: try Simple XML library instead of JAXB. My experience with it is the best.

like image 156
vektor Avatar answered Oct 04 '22 10:10

vektor