让XmlJavaTypeAdapter和接口很好地播放

时间:2012-08-15 13:27:15

标签: java jaxb

我有一个父子关系,其中子类:Foo是不可变的,并且没有定义默认的无参数构造函数。父类:Bar通过接口引用子进程:IFoo。我已经定义了一个适配器来解决构造函数问题,但现在遇到另一个问题,即JAXB抱怨这个上下文不知道类Foo。

如果我尝试使用JAXBContext引导我的Foo.class来解决这个问题,那么我会收到缺少默认构造函数错误。

请注意,我正在尝试遵循3.2.1 of the Unofficial JAXB Guide中描述的界面映射方法。

我是否需要采用不同的接口映射方法来解决此问题?我怀疑使用XmlRootElement标记每个接口实现意味着我的适配器代码没有运行(如Blaise Doughan here所述)。这让我想知道这两种方法是否本质上是不兼容的,我需要使用所描述的其他接口映射技术之一。

public interface IFoo {
  String getName();
  int getAge();
}

@XmlJavaTypeAdapter(FooAdapter.class)
@XmlRootElement
public class Foo implements IFoo {
  private final String name;
  private final int age;

  public Foo(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() { return name; }
  public int getAge() { return age; }
}

public class FooAdapter extends XmlAdapter<AdaptedFoo, Foo> {
  @Override
  public Foo unmarshal(AdaptedFoo af) throws Exception {
    return new Foo(af.getName(), af.getAge());
  }

  @Override
  public AdaptedFoo marshal(Foo foo) throws Exception {
    AdaptedFoo ret = new AdaptedFoo();
    ret.setName(foo.getName());
    ret.setAge(foo.getAge());
    return ret;
  }
}

public class AdaptedFoo {
  private String name;
  private int age;

  public AdaptedFoo() {}

  @XmlAttribute
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  @XmlAttribute
  public int getAge() { return age; }
  public void setAge(int age) { this.age = age; }
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Bar {
  @XmlAnyElement
  private IFoo foo;
  private int baz;

  public Bar() {}

  public IFoo getFoo() { return foo; }
  public void setFoo(IFoo foo) { this.foo = foo; }

  public int getBaz() { return baz; }
  public void setBaz(int baz) { this.baz = baz; }
}

public class Marshal {
  public static void main(String[] args) {
    Foo foo = new Foo("Adam", 34);
    Bar bar = new Bar();
    bar.setFoo(foo);
    bar.setBaz(10);

    try {
      JAXBContext jaxbContext = JAXBContext.newInstance(Bar.class, AdaptedFoo.class);
      Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

      // output pretty printed
      jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

      jaxbMarshaller.marshal(bar, System.out);
    } catch (JAXBException e) {
      e.printStackTrace();
    }
  }
}

堆栈跟踪

$ java Marshal
javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: class Foo nor any of its super class is known to this context.
javax.xml.bind.JAXBException: class Foo nor any of its super class is known to this context.]
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:311)
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
        at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
        at Marshal.main(Marshal.java:20)
Caused by: com.sun.istack.internal.SAXException2: class Foo nor any of its super class is known to this context.
javax.xml.bind.JAXBException: class Foo nor any of its super class is known to this context.
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:235)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:250)
        at com.sun.xml.internal.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodePr
operty.java:102)
        at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:341)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:582)
        at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:323)
        at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:483)
        at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
        ... 3 more
Caused by: javax.xml.bind.JAXBException: class Foo nor any of its super class is known to this context.
        at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:573)
        at com.sun.xml.internal.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodePr
operty.java:94)
        ... 8 more

1 个答案:

答案 0 :(得分:4)

如果您在@XmlElement属性上使用foo注释并指定实现类型,那么您的用例应该有用。

  @XmlElement(type=Foo.class)
  private IFoo foo;

<强>酒吧

以下是Bar

的更新版本
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Bar {
  @XmlElement(type=Foo.class)
  private IFoo foo;
  private int baz;

  public Bar() {}

  public IFoo getFoo() { return foo; }
  public void setFoo(IFoo foo) { this.foo = foo; }

  public int getBaz() { return baz; }
  public void setBaz(int baz) { this.baz = baz; }
}

了解更多信息