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

Posted

技术标签:

【中文标题】使用 JAXB 支持具有微小变化的模式【英文标题】:Using JAXB to support schemas with minor variations 【发布时间】:2012-03-14 06:04:01 【问题描述】:

情况

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

问题

我打算使用带有继承的 JAXB 2(来自 Metro)作为解决方案。我预计包结构最终会是这样的:

    com.company.xml.schema.v1
    com.company.xml.schema.v2
    com.company.xml.schema.v2.vendorxyz

v2 包中的类将简单地扩展 v1 包中的类并根据需要进行覆盖。不幸的是,这个计划最终是不可能的,因为子类不能覆盖父类中的注释(see here)。例如,如果架构中的一个属性在不同版本之间被重命名,那么 v2 元素类将不得不完全重新实现该元素,而不是从 v1 继承。

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

选项 1

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

选项 2

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

问题

我应该如何使用 JAXB 来支持具有细微变化的多个模式,而不会出现大量代码重复? 我应该考虑其他不同的技术组合吗?

编辑

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

com.company.xml.schema.v1.ElementA
com.company.xml.schema.v2.ElementA

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

在这种情况下使用 MOXy 的 OXM 会偶然发现一个错误,可以在 here 找到解决方法(同样使用 Blaise 提供的解决方案!)

【问题讨论】:

【参考方案1】:

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

您可以使用 EclipseLink JAXB 中的外部绑定文档来映射 XML Schema 之间的变化。

供应商 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;
    


供应商 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>

供应商 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>

演示

下面的示例代码演示了如何指定外部元数据。请注意我是如何介绍第四家供应商来展示可以合并外部元数据文档的。

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();
    


输出

以下是每个供应商的输出。请记住,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>

更多信息

http://blog.bdoughan.com/search/label/jaxb.properties http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html http://blog.bdoughan.com/2011/09/mapping-objects-to-multiple-xml-schemas.html

【讨论】:

非常感谢您的回答和样品。我已按照您的建议迁移到 MOXy 的 JAXB 实现,并且发现外部元数据支持非常强大。 谢谢,这对我有帮助。只有一件事:从 2.4 版开始,JAXBContextFactory.ECLIPSELINK_OXM_XML_KEYJAXBContextProperties.OXM_METADATA_SOURCE 取代。

以上是关于使用 JAXB 支持具有微小变化的模式的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用 JAXB 从模式映射到 java.util.Map?

如何使用 JAXB 从 XML 模式生成 Java 枚举?

Jaxb:在同一个包中解组具有多个命名空间的 xml

解组非根元素时的 JAXB 模式验证

JAXB 和编组

C# 中的 Jaxb 等效项