JAXB纯粹从接口编组

我有一个复杂的Java接口层次结构,我想用JAXB编组(而不一定是unmarshal)。 这些接口表示将从JAX-RS REST API返回的对象,如XML,JSON,YAML等。(我正在使用RestEasy,它可以用XML以外的格式封送JAXB注释类型。)

问题似乎是JAXB基本上是面向类的。 我已经对JAXB和接口的困难进行了大量的网络研究,最接近的解决方案是MOXy JAXB – 映射接口到XML和JAXB以及接口前端模型 。 但是,我有两个主要问题:a)我想在接口方面注释/工作,而不是具体的类(其中将有多个实现并包含不应该被编组的重要的其他状态),以及b)我有多级接口inheritance。 这是接口的示例,减去目前为止的任何JAXB注释:

interface Uuided { UUID getId(); } interface Named { String getName(); } interface Component extends Uuided, Named { Map getAttributes(); } interface Attribute extends Named { Type getType(); Object getValue(); } interface ComponentAttribute extends Attribute { Component getDeclaringComponent(); } 

理想情况下,这会产生类似于:

     green     

很明显,在摘要中,这会导致诸如确定最衍生的注释界面之类的问题,理论上这些界面可能不止一个。 但是,在我的情况下,我认为具体类只实现了一个应该封送的单个接口。 解组不是必需的,因为我有单独的类定义upsert属性。

所以我的问题是,这甚至可以用JAXB处理,如果是这样,怎么样? 即使我必须非常明确地定义绑定,适配器等,我也希望在JAXB框架内工作以获得RestEasy中所有非XML提供程序的好处。

简短回答:在接口字段中使用@XmlElement(type = Object.class)

详情如下:

我找到了两种方法可以使JAXB序列化您的接口:

  1. @XmlAnyElement
  2. @XmlElement(type = Object.class)

1. @ XmlAnyElement将

只需使用@XmlAnyElement注释您的接口类型字段,JAXB将从其具体类型序列化接口。 不要忘记使用@XmlRootElement注释具体类型,并将具体类型添加到JAXBContext。 完整示例如下:

 public class InterfaceSerializer { @XmlRootElement public static class Pojo { Pojo() { field1 = new PojoFieldImpl1(); field2 = new PojoFieldImpl2(); field3 = new PojoFieldImpl1(); } @XmlAnyElement public IPojoField field1; @XmlAnyElement public IPojoField field2; @XmlAnyElement public IPojoField field3; @Override public String toString() { return "field1 = " + field1 + "\nfield2 = " + field2 + "\nfield3 = " + field3; } } public static interface IPojoField { } @XmlRootElement public static class PojoFieldImpl1 implements IPojoField { PojoFieldImpl1() { value = "PojoFieldImpl1 value"; } public String value; @Override public String toString() { return value; } } @XmlRootElement public static class PojoFieldImpl2 implements IPojoField { PojoFieldImpl2() { value = "PojoFieldImpl2 value1"; value2 = "PojoFieldImpl2 value2"; } public String value; public String value2; @Override public String toString() { return value + " " + value2; } } public static void main(String []args) throws JAXBException { Pojo pojo = new Pojo(); JAXBContext jaxbContext = JAXBContext.newInstance(Pojo.class, PojoFieldImpl1.class, PojoFieldImpl2.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.marshal(pojo, new File("interfaceSerializer.xml")); } } 

输出XML:

    PojoFieldImpl1 value   PojoFieldImpl2 value1 PojoFieldImpl2 value2   PojoFieldImpl1 value   

这种方法的缺点:

  • 你无法从你的pojo中区分每个字段的XML(相同的实现将使用相同的标记编写)
  • 您没有任何类型信息来解组您的XML(如果您希望这样做)

这些缺点在第二个解决方案中得到修复:

2. @ XmlElement(type = Object.class)

我在mikesir87的博客文章中偶然发现了这一点。 只需用@XmlElement(type = Object.class)替换上面的@XmlAnyElement注释你应该在上面的Pojo类中有这样的东西:

 @XmlElement(type = Object.class) public IPojoField field1; @XmlElement(type = Object.class) public IPojoField field2; @XmlElement(type = Object.class) public IPojoField field3; 

重新运行我们的示例,生成的XML:

    PojoFieldImpl1 value   PojoFieldImpl2 value1 PojoFieldImpl2 value2   PojoFieldImpl1 value   

这也可以反序列化:

 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); Pojo unmarshalledPojo = (Pojo) unmarshaller.unmarshal(new File("interfaceSerializer.xml")); System.out.println(unmarshalledPojo); 

结果输出:

 field1 = PojoFieldImpl1 value field2 = PojoFieldImpl2 value1 PojoFieldImpl2 value2 field3 = PojoFieldImpl1 value 

可能是一个“ hackish ”解决方案,但它完成了工作。

我认为答案是JAXB根本不打算支持这一点,并试图强迫它是愚蠢的。 此外,JAXB驱动的JSON封送结果也不是理想的。

我最终编写了自己的编组框架,带有自己的一组注释:

 @MarshalMixin // marshal fields but not a top-level object interface Uuided { @MarshalAsString // ignore properties; just call toString() @MarshalId // treat as identifier for @MarshalUsingIds or cyclic ref UUID getId(); } @MarshalMixin interface Named { @MarshalId String getName(); } @MarshalObject // top-level marshaled object providing class name interface Component extends Uuided, Named { @MarshalAsKeyedObjectMap(key = "name") // see description below Map getAttributes(); } @MarshalObject interface Attribute extends Named { Type getType(); @MarshalDynamic // use run-time (not declared) type Object getValue(); } interface ComponentAttribute extends Attribute { @MarshalUsingIds Component getDeclaringComponent(); } 

生成的编组程序写入抽象层(当前为JSON和XML实现)。 这样可以提供更大的灵活性,使输出在不需要大量注释和适配器的情况下适用于不同的表示。 例如,我正在调用键控对象映射,其中每个对象都包含其映射键。 在JSON中,您需要一个映射,但在XML中,您需要一个序列:

 {..., map: {'a': {'name': 'a', ...}, 'b': {'name: 'b', ...}, ...}, ...} ............ 

似乎有多达4个人也关心这个,所以希望我最终可以开源。 🙂