首页 文章

JAXB纯粹从接口编组

提问于
浏览
11

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

问题似乎是JAXB基本上是面向类的 . 我已经对JAXB和接口的困难进行了大量的网络研究,最接近的解决方案是MOXy JAXB - Map Interfaces to XMLJAXB 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提供程序的好处 .

2 回答

  • 0

    Short answer: 在您的界面字段中使用 @XmlElement(type = Object.class) .

    详情如下:

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

    • @XmlAnyElement

    • @XmlElement(type = Object.class)

    1.@XmlAnyElement

    只需使用 @XmlAnyElement 注释您的接口类型字段,JAXB将从中序列化接口's concrete type. Don'忘记使用 @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>
    

    这种方法的缺点:

    • 你无法从你的pojo中区分每个字段的XML(相同的实现将使用相同的标记编写)

    • 您没有任何类型信息来解组您的XML(如果您希望这样做)

    这些缺点在 second solution 中得到修复:

    2.@XmlElement(type = Object.class)

    我在mikesir87's blog post偶然发现了这件事 . 只需用 @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:

    <?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
    

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

  • 4

    我认为答案是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个人也关心这个,所以希望我最终可以开源 . :-)

相关问题