Использование JAXB для поддержки схем с небольшими изменениями - PullRequest
11 голосов
/ 23 февраля 2012

Ситуация

Мне нужно поддерживать создание документов XML на основе схем, которые незначительно отличаются друг от друга. В частности, схемы, которые мне нужно поддерживать, основаны на отраслевых стандартах, которые со временем меняются незначительно, и поставщики могут создавать свои собственные настраиваемые версии.

Проблема

Я собирался использовать JAXB 2 (из Metro) с наследованием в качестве решения. Я ожидал, что структура пакета будет выглядеть примерно так:

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

Где классы в пакете v2 будут просто расширять классы в пакете v1 и при необходимости переопределять. К сожалению, этот план оказался невозможным, поскольку подклассы не могут перезаписать аннотации в родительских классах ( см. Здесь ). Например, если атрибут в схеме был переименован между версиями, то класс элемента v2 должен был бы полностью повторно реализовать элемент без наследования от v1.

Так что, насколько я могу судить, у меня остается только два варианта

Вариант 1

Создайте «базовый» пакет для каждого типа схемы, аннотируйте классы элементов в этом пакете с помощью @XmlAccessorType (XmlAccessType.NONE) и удалите все другие аннотации. Затем в каждом версионном пакете создайте классы, которые подклассируют соответствующий класс в «базовом» пакете, и добавьте все необходимые аннотации. Это решение немного помогает мне в области наследования, но дублирование кода огромно, и его было бы сложно поддерживать.

Вариант 2

Не используйте JAXB. Мне действительно не нравится это решение, так как я также хотел бы работать с JAX-RS / JAX-WS.

Вопросы

  • Как мне использовать JAXB для поддержки нескольких схем с небольшими изменениями, без дублирования кода?
  • Есть ли какая-то другая комбинация технологий, на которую я должен обратить внимание?

EDIT

Приведенное ниже решение от Blaise отлично сработало для большинства наших схем, которые представляли собой лишь незначительный перевод друг друга с обычно одинаковыми данными. Однако мы столкнулись с проблемой в тех случаях, когда было более целесообразно использовать наследование с именами пакетов для управления версиями. Например:

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

(где v2.ElementA расширяет v1.ElementA)

Использование OXM MOXy в этом случае наталкивается на ошибку, и можно найти обходной путь здесь (с решением, предоставленным Blaise, не меньше!)

1 Ответ

5 голосов
/ 24 февраля 2012

Примечание: Я 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;
    }

}

Поставщик 2

Мы будем использовать внешние метаданные MOXy для настройки метаданных, предоставляемых аннотациями. В приведенном ниже документе (oxm-v2.xml) мы сопоставим свойства firstName и lastName с атрибутами 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>

Для получения дополнительной информации

...