Подтвердить что ты не робот

Сериализовать Jaxb Pojos для нескольких или разных пространств имен

Рассмотрим следующий код:

Main.java
====
package com.sample;

import com.sample.entity.Customer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Main {    

    public static void main(String[] args) throws JAXBException {
       JAXBContext jc = JAXBContext.newInstance(Customer.class);

       Customer customer = new Customer();
       customer.setId(123);

       Marshaller m = jc.createMarshaller();
       m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
       m.marshal(customer, System.out);    
    }
}


Customer.java
====
package com.sample.entity;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {
    private long id;

    public long getId() {
        return id;
    }

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

package-info.java
====
@XmlSchema(namespace = "http://www.example.org/package", elementFormDefault = XmlNsForm.QUALIFIED)
package com.sample.entity;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;

Я хочу повторно использовать Customer POJO и создать два разных значения пространства имен. Поэтому сначала я хочу напечатать выходные данные

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns="http://www.example.org/package">
    <id>123</id>
</customer>

как это делает текущий код, затем создайте второй xml с другим основным пространством имен из того же pojo, который будет выглядеть следующим образом:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns="http://www.another.org/package">
    <id>123</id>
</customer>

или удалить пространство имен вместе

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <id>123</id>
</customer>
4b9b3361

Ответ 1

Это хороший вопрос.

Вместо того, чтобы работать на уровне уровня, вы можете работать на уровне Jaxb node. Практически нет накладных расходов.

Ключ состоит в создании/доступе к элементам уровня JAXBElement (node) из классов бизнес-логики и настройке XML-Node, которые он представляет с помощью определения QName.

QName предоставляет несколько вариантов настройки, начиная от полных имен пространств имен и без пространства имен. Единственное предостережение в том, что использование пространства имен без префикса невозможно. Jaxb будет использовать пространство имен по умолчанию, если вы его не предоставили.

Пример:

QName elaborateQName = new QName("http://www.another.org/package", "customer", "myNs");
QName simpleQName = new QName("customer");

JAXBElement jx1 = new JAXBElement(elaborateQName, customer.getClass(), customer);
JAXBElement jx2 = new JAXBElement(simpleQName, customer.getClass(), customer);

m.marshal(jx1, System.out);
m.marshal(jx2, System.out);

Это приведет к следующему выводу:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myNs:customer xmlns:myNs="http://www.another.org/package">
    <id>123</id>
</myNs:customer>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <id>123</id>
</customer>

Использование пространства имен без префикса невозможно. Попытка QName с пустым префиксом или эквивалентным XMLConstants.DEFAULT_NS_PREFIX в качестве параметра приведет к генерированию префикса по умолчанию (например, ns1):

 // QName defaultNs = new QName("http://www.another.org/package", "customer", "");

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:customer xmlns:ns1="http://www.another.org/package">
    <id>123</id>
</ns2:customer>

Использование null в качестве параметра для prefix дает исключение:

java.lang.IllegalArgumentException: prefix cannot be "null" when creating a QName
at javax.xml.namespace.QName.<init>(QName.java:251)

Ответ 2

Как переопределить атрибут root xmlns с помощью MOXy:

EclipseLink MOXy обеспечивает легкую настройку сортировки JAXB, включая изменение пространства имен по умолчанию вашего корневого элемента. Отображение Object to XML (OXM) для переопределения пространства имен по умолчанию:

<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5">
    <xml-schema element-form-default="QUALIFIED" namespace="http://www.another.org/package"/>
</xml-bindings>

Чтобы не иметь пространства имен по умолчанию, используйте <xml-schema element-form-default="UNSET"/>.

MOXy Setup:

1) добавьте библиотеку в свой проект:

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>org.eclipse.persistence.moxy</artifactId>
    <version>2.6.2</version>
</dependency>

2) Добавьте jaxb.properties в пакет объектной модели, чтобы включить MOXy factory (например, src/main/resources/com/sample/entity/jaxb.properties):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

3) Создайте свой контекст в соответствии с конфигурацией OXM (в этом примере файл OXM находится в src/main/resources/com/sample/entity/my-oxm.xml):

Map<String, Source> metadata = Collections.singletonMap("com.sample.entity", new StreamSource(Customer.class.getResourceAsStream("my-oxm.xml")));
Map<String, Object> properties = Collections.singletonMap(JAXBContextProperties.OXM_METADATA_SOURCE, metadata);
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] {customer.getClass()}, properties);

Затем вы можете использовать сортировку на JAXBContext как обычно. Создавайте отдельные контексты для каждого файла OXM, который вы хотите использовать.

Дополнительные настройки с MOXy:

Использование MOXy позволяет дополнительно настроить сортировку вашего объекта в будущем, не изменяя модель объекта, эффективно делая логику маршаллинга придерживаться принципа открытого/закрытого. даже если у вас нет явных классов Marshaller. Например, если вам необходимо маршалировать ваш объект Customer как Person, вы можете сделать это, добавив еще один файл OXM:

<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5">
    <xml-schema element-form-default="QUALIFIED" namespace="http://www.example.com/person"/>
    <java-types>
        <java-type name="com.sample.entity.Customer">
            <xml-root-element name="person"/>
            <java-attributes>
                <xml-attribute java-attribute="id" name="personId"/>
                <xml-element java-attribute="id" xml-path="someOtherId/text()"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>