18

I think this question has been asked like a million times, but none of solutions suggested worked for me. Here is my sample implementation

public class FooImpl2 implements Foo {
    private int a = 100 ;
    private String b = "I am FooImpl2";
    private boolean c;

    public int getA() {
        return a;
    }
    public void setA(int a) {
        this.a = a;
    }
    public String getB() {
        return b;
    }
    public void setB(String b) {
        this.b = b;
    }
    public boolean isC() {
        return c;
    }
    public void setC(boolean c) {
        this.c = c;
    }

}

@XmlRootElement
@XmlSeeAlso({FooImpl1.class, FooImpl2.class})
public interface Foo {}

public class FooImpl1 implements Foo {    
    private int x;
    private String y ="I am FooImpl1";
    private boolean z;

    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public String getY() {
        return y;
    }
    public void setY(String y) {
        this.y = y;
    }
    public boolean isZ() {
        return z;
    }
    public void setZ(boolean z) {
        this.z = z;
    }        
}

@XmlRootElement
public class Response{

    private Foo foo;

    @XmlElement(type=Object.class)
    public Foo getFoo() {
        return foo;
    }

    public void setFoo(Foo foo) {
        this.foo = foo;
    }

}

public class SimpleResource {    
    @Path("foo/{val}") @Produces({"application/json"}) @GET
    public FooAdapter getFoo(@QueryParam("val") int val) {
        FooAdapter ret = new FooAdapter();
        if(val % 2 == 0) {
            ret.setFoo(new FooImpl2());
        } else {
            ret.setFoo(new FooImpl1());
        }

        return ret;
    }

I always get following exception

com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions com.abc.objectsToReturn.Foo is an interface,

can any one help me to figure out right solution

axtavt
  • 239,438
  • 41
  • 511
  • 482
user497760
  • 181
  • 1
  • 1
  • 3

2 Answers2

9

This isn't really an interface issue, you just need to change the way you bootstrap your JAXBContext.

If you change it to the following:

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(Response.class, FooImpl1.class, FooImpl2.class);

        Response response = new Response();
        FooImpl1 foo = new FooImpl1();
        response.setFoo(foo);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(response, System.out);
    }
}

Then you will get the following output (with any JAXB implementation: Metro, MOXy, etc):

<response>
   <foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="fooImpl1">
      <x>0</x>
      <y>I am FooImpl1</y>
      <z>false</z>
   </foo>
</response>

MOXy JAXB allows your entire model to be interfaces, checkout:

I also have a blog post that may be relevant to what you are trying to build:

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Blaise thanks for the example, however this did not resolve my issue :( – user497760 Nov 05 '10 at 13:56
  • What problems did you run into? You will need to include the eclipselink.jar on your class path, add the appropriate jaxb.properties, and implement the ObjectFactory to return instances of the impl classes. – bdoughan Nov 05 '10 at 14:05
  • I don't see any references to classes from eclipselink.jar in the sample code. Also how does ObjectFactory knows that it has to pickup jaxb.properties? – user497760 Nov 05 '10 at 14:51
  • 1
    JAXB is a specification with a standard runtime. A jaxb.properties is used to specify the JAXB runtime that should be used. Without a jaxb.properties file the default version of JAXB in the JRE is used. The ObjectFactory is used by JAXB runtimes to instantiate instances of the classes. – bdoughan Nov 05 '10 at 14:54
  • There is problem with org.eclipse.persistence.jaxb.javamodel.reflection.JavaClassImpl class – user497760 Nov 05 '10 at 21:05
  • getActualTypeArguments() method in this class does not seem to be handling sun.reflect.generics.reflectiveObjects.TypeVariableImpl – user497760 Nov 05 '10 at 21:06
  • 1
    I have revised my answer. Changing the way you bootstrap your JAXBContext will eliminate your problem with interfaces and be portable across any JAXB implementation. – bdoughan Nov 08 '10 at 16:19
  • @BlaiseDoughan, thanks for this helpful answer. I'm still reading through your blogposts linked above but can you tell me what to do if the interface is from a 3rd-party jar? – iamkenos May 16 '17 at 07:56
4

When you use interfaces just to hide your implementation classes from exposure, and when there's 1-to-1 (or close to 1-on-1) relationship between a class and an interface, XmlJavaTypeAdapter can be used like below.

@XmlJavaTypeAdapter(FooImpl.Adapter.class)
interface IFoo {
  ...
}
class FooImpl implements IFoo {
  @XmlAttribute
  private String name;
  @XmlElement
  private int x;

  ...

  static class Adapter extends XmlAdapter<FooImpl,IFoo> {
    IFoo unmarshal(FooImpl v) { return v; }
    FooImpl marshal(IFoo v) { return (FooImpl)v; }
  }
}

class Somewhere {
  public IFoo lhs;
  public IFoo rhs;
}
Aldrin Rodrigues
  • 151
  • 1
  • 11
  • 2
    Which is an exact copy of: https://jaxb.java.net/guide/Mapping_interfaces.html#Use__XmlJavaTypeAdapter. You should always cite your sources – Murmel Aug 26 '16 at 16:27