Java / Jersey / JAXB / MOXy - 给定基类对象的序列化派生成员

时间:2013-06-26 15:14:05

标签: xml serialization jaxb jersey moxy

我有一个RESTful Jersey Web服务,它以XML格式返回对象。返回对象的一个​​成员是几种可能的类型之一(都来自公共基类),直到运行时才知道。我无法让这个成员出现在输出XML中,我将在下面显示。

我已将问题减少到一些示例类:

@XmlRootElement
public abstract class Animal {

    public String type;

    public Animal() {
        type = "?";
    }

    public Animal(String type) {
        this.type = type;
    }
}

@XmlRootElement
public class Mammal extends Animal {

    public String name;

    public Mammal() {
        name = "?";
    }

    public Mammal(String type, String name) {
        super(type);
        this.name = name;
    }
}

@XmlRootElement
public class Zoo {
    private Animal creature;

    @XmlElementRef
    public Animal getCreature() {
        return creature;
    }

    public void setCreature(Animal creature) {
        this.creature = creature;
    }

    public Zoo() {
        creature = new Mammal("Mouse", "Mickey");
    }
}

@Path("/test")
public class TestResource {

    @GET
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public Zoo get() {
        Zoo z = new Zoo();
        return z;
    }
}

当我致电该服务时,我得到以下回复:

<zoo/>

不包括预期的生物。

如果我将成员'creature'的类型从Animal(抽象基类)更改为Mammal(派生类),那么我得到预期的输出,但这不是一个合适的解决方案。

所以我将以下代码添加到get()方法中:

    // DIAGNOSTIC CODE: Convert object to XML and send to System.out
    try {
        JAXBContext context = JAXBContext.newInstance(Zoo.class, Mammal.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.marshal(z, System.out);
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }

现在即使服务中的XML错误(只是空标记),我在控制台中看到了正确的输出:

<?xml version="1.0" encoding="UTF-8"?>
<zoo>
   <mammal>
      <type>Mouse</type>
      <name>Mickey</name>
   </mammal>
</zoo>

现在,为了获得该输出,我必须确保将Mammal.class传递给JAXBContext.newInstance(),否则我在输出中得到相同的空标记,所以我猜测问题是Web服务完成的XML序列化不知道派生的Mammal类,因此无法正确序列化对象。这是正确的诊断吗?我如何解决它?

1 个答案:

答案 0 :(得分:2)

问题是Jersey将从返回类型JAXBContext创建的默认Zoo将不会知道Mammal类。它会引入Animal,但不会引入任何子类(因为不存在这样做的API)。这就是为什么当您创建JAXBContext知道Mammal用于记录目的时,一切都正常工作的原因。

解决方案#1 - 利用@XmlSeeAlso

@XmlSeeAlso注释告诉JAXB impl,当你处理这个类时也处理这些其他类。通常,这用于引用映射的子类。

@XmlRootElement
@XmlSeeAlso({Mammal.class})
public abstract class Animal {

    public String type;

    public Animal() {
        type = "?";
    }

    public Animal(String type) {
        this.type = type;
    }
}

解决方案#2 - 利用JAX-RS ContextResolver

我提到问题是由于Jersey创建的默认JAXBContext不会知道Mammal类。您可以使用ContextResolver返回JAXBContext。以下是创建ContextResolver的示例的链接: