我有一个复杂的Java接口层次结构,我想用JAXB编组(而不一定是unmarshal)。这些接口表示将从JAX-RS REST API以XML,JSON,YAML等形式返回的对象。(我正在使用RestEasy,它可以用XML以外的格式封送JAXB注释类型。)
问题似乎是JAXB基本上是面向类的。我已经对JAXB和接口的困难进行了大量的网络研究,最接近的解决方案是MOXy JAXB - Map Interfaces to XML和JAXB and Interface Fronted Models。但是,我有两个主要问题:a)我想在接口方面注释/工作,而不是具体的类(其中将有多个实现并包含不应该被编组的重要的其他状态),以及b)我有多级接口继承。这是接口的示例,减去目前为止的任何JAXB注释:
interface Uuided {
UUID getId();
}
interface Named {
String getName();
}
interface Component extends Uuided, Named {
Map<String, ComponentAttribute> getAttributes();
}
interface Attribute extends Named {
Type getType();
Object getValue();
}
interface ComponentAttribute extends Attribute {
Component getDeclaringComponent();
}
理想情况下,这会产生类似:
<component id="xxx" name="thing">
<attributes>
<componentAttribute name="color">
<type><stringType/></type>
<value>green</value>
<declaringComponent idref="xxx"/>
</componentAttribute>
</attributes>
</component>
显然,在摘要中,这会导致诸如确定最多派生注释界面之类的问题,理论上可能不止一个。但是,在我的情况下,我认为具体类只实现了一个应该被封送的单个接口。解组不是必需的,因为我有单独的类定义upsert属性。
所以我的问题是,这甚至可以用JAXB来处理,如果是这样,怎么样?即使我必须非常明确地定义绑定,适配器等,我也希望在JAXB框架内工作以获得RestEasy中所有非XML提供程序的好处。
答案 0 :(得分:5)
简答:在您的界面字段中使用@XmlElement(type = Object.class)
。
以下详细信息:
我找到了两种方法可以使JAXB序列化你的接口:
@XmlAnyElement
@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:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pojo>
<pojoFieldImpl1>
<value>PojoFieldImpl1 value</value>
</pojoFieldImpl1>
<pojoFieldImpl2>
<value>PojoFieldImpl2 value1</value>
<value2>PojoFieldImpl2 value2</value2>
</pojoFieldImpl2>
<pojoFieldImpl1>
<value>PojoFieldImpl1 value</value>
</pojoFieldImpl1>
</pojo>
此方法的缺点:
这些缺点已在第二个解决方案中修复:
2. @ XmlElement(type = Object.class)
我在mikesir87's blog post中偶然发现了这一点。只需使用 @XmlAnyElement
替换上面的 @XmlElement(type = Object.class)
注释
你应该从上面的Pojo类中得到类似的东西:
@XmlElement(type = Object.class)
public IPojoField field1;
@XmlElement(type = Object.class)
public IPojoField field2;
@XmlElement(type = Object.class)
public IPojoField field3;
重新运行我们的示例,生成的XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pojo>
<field1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1">
<value>PojoFieldImpl1 value</value>
</field1>
<field2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl2">
<value>PojoFieldImpl2 value1</value>
<value2>PojoFieldImpl2 value2</value2>
</field2>
<field3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pojoFieldImpl1">
<value>PojoFieldImpl1 value</value>
</field3>
</pojo>
这也可以反序列化:
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
可能是&#34; hackish &#34;解决方案,但它完成了工作。
答案 1 :(得分:1)
我认为答案是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<String, ComponentAttribute> 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', ...}, ...}, ...}
...<map><thing name='a'>...</thing><thing name='b'>...</thing></map>...
似乎有多达4个人也关心这个,所以希望我最终可以开源。 : - )