使用JAXB来支持具有微小变化的模式

Ter*_*nce 12 java schema design-patterns xml-serialization jaxb2

情况

我需要支持基于模式生成XML文档,这些模式之间只是略有不同.具体来说,我需要支持的模式基于行业标准,这些标准随着时间的推移会略有变化,供应商可能会制作自己的定制版本.

问题

我打算使用JAXB 2(来自Metro)继承作为解决方案.我期望包结构最终会像这样:

    com.company.xml.schema.v1
    com.company.xml.schema.v2
    com.company.xml.schema.v2.vendorxyz
Run Code Online (Sandbox Code Playgroud)

其中v2包中的类只是扩展v1包中的类并根据需要覆盖.不幸的是,该计划最终变得不可能,因为子类不能覆盖父类中的注释(参见此处).例如,如果模式中的属性在版本之间重命名,则v2元素类必须完全重新实现该元素而不从v1继承.

据我所知,这让我只有两个选择

选项1

为每个模式类型创建一个"基础"包,使用@XmlAccessorType(XmlAccessType.NONE)注释该包中的元素类,并删除所有其他注释.然后,在每个版本化的包中创建类,这些类将"base"包中的相应类子类化,并添加所有必需的注释.这个解决方案确实给了我一些继承领域的帮助,但代码重复是巨大的,维护是一个挑战.

选项2

不要使用JAXB.我真的不喜欢这个解决方案,因为我也想使用JAX-RS/JAX-WS.

问题

  • 我应该如何使用JAXB来支持具有微小变化的多个模式,而不需要一堆代码重复?
  • 我应该看一下不同的技术组合吗?

编辑

Blaise下面的解决方案非常适用于我们的大多数模式,这些模式只是彼此之间的一个小翻译,通常具有相同的数据.但是,如果将继承与包名称用于版本控制更有意义,我们遇到了一个问题.例如:

com.company.xml.schema.v1.ElementA
com.company.xml.schema.v2.ElementA
Run Code Online (Sandbox Code Playgroud)

(其中v2.ElementA扩展v1.ElementA)

在这种情况下使用MOXy的OXM会遇到错误,可以在这里找到解决方法(使用Blaise提供的解决方案,不能少!)

bdo*_*han 5

注意: 我是EclipseLink JAXB(MOXy)的负责人,也是JAXB 2(JSR-222)专家组的成员.

您可以使用EclipseLink JAXB中的外部绑定文档来映射XML模式之间的变体.

供应商1

您可以使用标准JAXB注释来映射其中一个供应商:

package forum9419732;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlAttribute
    private int id;

    private String lastName;
    private String firstName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

}
Run Code Online (Sandbox Code Playgroud)

供应商2

我们将使用MOXy的外部元数据来自定义注释提供的元数据.在oxm-v2.xml下面的文档()中,我们将firstNamelastName属性映射到XML属性:

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum9419732">
    <java-types>
        <java-type name="Customer">
            <java-attributes>
                <xml-attribute java-attribute="firstName"/>
                <xml-attribute java-attribute="lastName"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>
Run Code Online (Sandbox Code Playgroud)

供应商3

我们将再次使用MOXy的外部绑定文档(oxm-v3.xml)来覆盖注释.这次我们将使id属性成为XML元素.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum9419732">
    <java-types>
        <java-type name="Customer">
            <java-attributes>
                <xml-element java-attribute="id" name="identifier"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>
Run Code Online (Sandbox Code Playgroud)

演示

下面的示例代码演示了如何指定外部元数据.请注意我是如何介绍第四个供应商以显示可以组合外部元数据文档的.

package forum9419732;

import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws JAXBException {
        Customer customer = new Customer();
        customer.setId(123);
        customer.setFirstName("Jane");
        customer.setLastName("Doe");

        // VENDOR 1
        JAXBContext jcV1 = JAXBContext.newInstance(Customer.class);
        marshal(jcV1, customer);

        // VENDOR 2
        Map<String, Object> propertiesV2 = new HashMap<String, Object>(1);
        propertiesV2.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum9419732/oxm-v2.xml");
        JAXBContext jcV2 = JAXBContext.newInstance(new Class[] {Customer.class}, propertiesV2);
        marshal(jcV2, customer);

        // VENDOR 3
        Map<String, Object> propertiesV3 = new HashMap<String, Object>(1);
        propertiesV3.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum9419732/oxm-v3.xml");
        JAXBContext jcV3 = JAXBContext.newInstance(new Class[] {Customer.class}, propertiesV3);
        marshal(jcV3, customer);

        // VENDOR 4
        Map<String, Object> propertiesV4 = new HashMap<String, Object>(1);
        List<String> oxmV4 = new ArrayList<String>(2);
        oxmV4.add("forum9419732/oxm-v2.xml");
        oxmV4.add("forum9419732/oxm-v3.xml");
        propertiesV4.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, oxmV4);
        JAXBContext jcV4 = JAXBContext.newInstance(new Class[] {Customer.class}, propertiesV4);
        marshal(jcV4, customer);
    }

    private static void marshal(JAXBContext jc, Customer customer) throws JAXBException {
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
        System.out.println();
    }

}
Run Code Online (Sandbox Code Playgroud)

产量

以下是每个供应商的输出.请记住,相同的实例Customer用于制作每个XML文档.

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
   <lastName>Doe</lastName>
   <firstName>Jane</firstName>
</customer>

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123" lastName="Doe" firstName="Jane"/>

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <identifier>123</identifier>
   <lastName>Doe</lastName>
   <firstName>Jane</firstName>
</customer>

<?xml version="1.0" encoding="UTF-8"?>
<customer lastName="Doe" firstName="Jane">
   <identifier>123</identifier>
</customer>
Run Code Online (Sandbox Code Playgroud)

欲获得更多信息