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

标签 xml serialization jaxb jersey moxy

我有一个 RESTful Jersey Web 服务,它以 XML 形式返回对象。返回对象的成员之一是几种可能的类型之一(全部派生自公共(public)基类),直到运行时才知道。我无法让该成员出现在输出 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/>

其中不包括预期的生物。

如果我将成员“生物”的类型从 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 类,因此无法正确序列化对象。这是正确的诊断吗?我如何解决它?

最佳答案

问题是 Jersey 将从返回类型 Zoo 创建的默认 JAXBContext 将不知道 Mammal 类。它将引入 Animal,但不会引入其任何子类(因为不存在可以执行此操作的 API)。这就是为什么当您创建一个了解 MammalJAXBContext 来进行日志记录时,一切都正常工作。

解决方案#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 示例的链接:

关于xml - Java/Jersey/JAXB/MOXy - 序列化给定基类对象的派生成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17323991/

相关文章:

java - 解码 xml 时出现 UnmarshalException

c# - 您可以为日期时间的 XmlSerialization 指定格式吗?

java - JAXB 中不区分大小写的 XMLEnumValue

html - 将一个标签的 css 样式用于其他标签?

xml - 如何使用 xml-model 标签将 xml 文档链接到 Relax NG 模式?

asp.net - 使属性可反序列化但不可序列化

python - DRF 使 DateTimeField 变得简单,而不考虑默认时区

Java 11 : import javax. xml.ws.WebFault: "Cannot resolve symbol ws"

c++ - 解析xml到对象,打印标签之间的内容-QT

c# - 将一个 XML 文档转换为另一个 XML 文档