10

This is my scenario. I have a generic class:

public class Tuple<T> extends ArrayList<T> {
  //...
  public Tuple(T ...members) {
    this(Arrays.asList(members));
  }

  @XmlElementWrapper(name = "tuple")
  @XmlElement(name = "value")
  public List<T> getList() {
    return this;
  }
}

And a child class:

public class StringTuple extends Tuple<String> {
  public StringTuple(String ...members) {
    super(members);
  }

  //explanation of why overriding this method soon ...
  @XmlElementWrapper(name = "tuple")
  @XmlElement(name = "value")
  @Override
  public List<String> getList() {
    return this;
  }
}

These classes are referenced here:

@XmlRootElement(namespace = "iv4e.xml.jaxb.model")
public class Relation {
  private Tuple<StringTuple> relationVars;
  //...
  @XmlElementWrapper(name = "allRelationVars")
  @XmlElement(name = "relationVarsList")
  public Tuple<StringTuple> getRelationVars() {
    return relationVars;
  }
}

Then a Relation object is created with something like:

Relation rel = new Relation();
rel.setRelationVars(new Tuple<StringTuple>(
  new StringTuple("RelationshipVar1"), new StringTuple("RelationshipVar2")));

After marshalling this object, the Xml output is the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:relation xmlns:ns2="iv4e.xml.jaxb.model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="">

  <allRelationVars>
    <relationVarsList>
        <tuple>
            <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">RelationshipVar1</value>
        </tuple>
        <tuple>
            <value>RelationshipVar1</value>
        </tuple>
    </relationVarsList>
    <relationVarsList>
        <tuple>
            <value xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">RelationshipVar2</value>
        </tuple>
        <tuple>
            <value>RelationshipVar2</value>
        </tuple>
    </relationVarsList>
  </allRelationVars>

</ns2:relation>

So the value elements are duplicated!.

Now, the reason the class StringTuple overrides List<T> getList() with List<String> getList() is avoiding the annoying generated xmlns:xs attributes in every member of the list (the value elements in the xml document). But then every member of the list is shown twice in the output. Apparently, it is because both the overridden parent method and the child method are annotated with @XmlElement. So my main question is: there is a way to ignore overridden methods annotated with @XmlElement in Jaxb ? (considering that the overridding method is also annotated with @XmlElement)

I found an old post reporting quite a similar problem: http://old.nabble.com/@XmlElement-on-overridden-methods-td19101616.html , but I have not found any solution yet. Also note that adding a @XmlTransient annotation to the getList method at the parent class (Tuple<T>) could solve this problem but will generate others, since the parent class is not abstract and is used alone in other contexts.

One side secondary question: is it possible to declare the xmlns:xs attribute at the root node instead of it -annoyingly- appearing in every node where it is needed? I know this can be done with the NamespacePrefixMapper class, but since it is a non standard, SUN internal class, I rather prefer to use a more implementation independent approach.

Thanks in advance for any feedback !

Sergio
  • 8,532
  • 11
  • 52
  • 94
  • 1
    Before I get too far into this issue, do your domain objects need to extend `ArrayList`? Why does `Tuple` extend `ArrayList` instead of having a property of type `ArrayList`? – bdoughan Oct 21 '11 at 16:25
  • Hi Blaise!, I chose inheriting from ArrayList since my Tuple object is in fact an ArrayList. However, I could live with it containing an ArrayList instead, though then I would have to implement some delegation methods. Anyway, that would not solve my problem since I still would have a Tuple generic class, and a StringTuple extending Tuple. – Sergio Oct 21 '11 at 17:29
  • I have added an answer with an approach that could be used to get rid of the `xsi:type` declaration. – bdoughan Oct 21 '11 at 18:03
  • I think, this dual behaviour is not possible with JAXB: the property is either mapped or not. I usually try to annotate only leave classes in class hierarchy: you can always achieve this (e.g. by repeating getters which will be a trivial `super` call). Perhaps consider refactoring? – dma_k Oct 23 '11 at 23:52
  • I see. I think it is a little bit strange how JAXB manages inheritance concerns. I will take your advice of only annotating the leaves, thanks for the feedback!!. – Sergio Oct 24 '11 at 12:11

2 Answers2

4

This might be quite old, but its the first result while searching for "JAXB duplicate fields"

Stumbled upon the same problem, this did the trick for me:

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE) // <-- made the difference
public abstract class ParentClass
{
...
}


@XmlRootElement
public class ChildClass extends ParentClass
{
 ...
}
Wolf
  • 991
  • 1
  • 9
  • 12
3

You could use the following approach of marking the property @XmlTransient on the parent and @XmlElement on the child:

Parent

package forum7851052;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlRootElement
public class Parent<T> {

    private List<T> item = new ArrayList<T>();

    @XmlTransient
    public List<T> getItem() {
        return item;
    }

    public void setItem(List<T> item) {
        this.item = item;
    }

}

IntegerChild

package forum7851052;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class IntegerChild extends Parent<Integer> {

    @Override
    @XmlElement
    public List<Integer> getItem() {
        return super.getItem();
    }

    @Override
    public void setItem(List<Integer> item) {
        super.setItem(item);
    }

}

StringChild

package forum7851052;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class StringChild extends Parent<String> {

    @Override
    @XmlElement
    public List<String> getItem() {
        return super.getItem();
    }

    @Override
    public void setItem(List<String> item) {
        super.setItem(item);
    }

}

Demo

package forum7851052;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Parent.class, IntegerChild.class, StringChild.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        IntegerChild integerChild = new IntegerChild();
        integerChild.getItem().add(1);
        integerChild.getItem().add(2);
        marshaller.marshal(integerChild, System.out);

        StringChild stringChild = new StringChild();
        stringChild.getItem().add("A");
        stringChild.getItem().add("B");
        marshaller.marshal(stringChild, System.out);
    }

}

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<integerChild>
    <item>1</item>
    <item>2</item>
</integerChild>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<stringChild>
    <item>A</item>
    <item>B</item>
</stringChild>
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • What if the Parent is used in serialization as well (making the parent's field transient is not an option)? That should be absolutely valid case, yet JAXB seems to ingore @Override annotations. – stuchl4n3k May 17 '15 at 14:17