java - 获取 XmlJavaTypeAdapter 和接口(interface)以很好地发挥作用

标签 java jaxb

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

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

请注意,我正在尝试遵循 3.2.1 of the Unofficial JAXB Guide 中描述的接口(interface)映射方法.

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

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

最佳答案

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

  @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; }
}

了解更多信息

关于java - 获取 XmlJavaTypeAdapter 和接口(interface)以很好地发挥作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11970119/

相关文章:

java - Android:如何创建 1 秒的计时器

java - 我已经构建了一个 WebView 应用程序,但网站下的弹出窗口未显示

Java XML 解析和原始字节偏移量

java - 当我的 pojos 分布在许多不同的包中时,将 jaxb.properties 放在哪里

java - 带有简单 XML 框架的复杂类型?

java - JRebel 与最新 weblogic 的兼容性

java - 频繁 Integer.toString() 转换的内存问题

java - 需要在网络服务中使用自定义类而不是生成(由 wsimport)

java - Mockito - 如何验证间接私有(private)字段对象进行的方法调用

xml - 如何使用 JAXB 解析带有命名空间的 XML