Статьи

JAXB и корневые элементы

@XmlRootElement — это аннотация, которую люди привыкли использовать с JAXB (JSR-222). Его цель — уникально связать корневой элемент с классом. Поскольку классы JAXB отображаются на сложные типы, класс может соответствовать нескольким корневым элементам. В этом случае @XmlRootElement нельзя использовать, и люди начинают немного запутываться. В этом посте я покажу, как @XmlElementDecl можно использовать для сопоставления этого варианта использования.

XML-схема

Схема XML ниже содержит три корневых элемента: покупатель , адрес выставления счета и адрес доставки . Элемент customer имеет анонимный сложный тип, в то время как адрес выставления счета и адрес доставки имеют одинаковый именованный тип (тип адреса ).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
    targetNamespace="http://www.example.org/customer"
    elementFormDefault="qualified">
 
    <xs:element name="customer">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="billing-address"/>
                <xs:element ref="shipping-address"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
    <xs:complexType name="address-type">
        <xs:sequence>
            <xs:element name="street" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
 
    <xs:element name="billing-address" type="address-type"/>
 
    <xs:element name="shipping-address" type="address-type"/>
 
</xs:schema>

Сгенерированная модель

Ниже приведена модель JAXB, созданная на основе схемы XML. Те же понятия применяются при добавлении аннотаций JAXB к существующей модели Java.

Клиент

Классы домена JAXB соответствуют сложным типам. Поскольку элемент customer имеет анонимный сложный тип, класс Customer имеет аннотацию @XmlRootElement . Это потому, что только один элемент XML может быть связан с анонимным типом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package org.example.customer;
 
import javax.xml.bind.annotation.*;
 
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {"billingAddress","shippingAddress"})
@XmlRootElement(name = "customer")
public class Customer {
 
    @XmlElement(name = "billing-address", required = true)
    protected AddressType billingAddress;
 
    @XmlElement(name = "shipping-address", required = true)
    protected AddressType shippingAddress;
 
    public AddressType getBillingAddress() {
        return billingAddress;
    }
 
    public void setBillingAddress(AddressType value) {
        this.billingAddress = value;
    }
 
    public AddressType getShippingAddress() {
        return shippingAddress;
    }
 
    public void setShippingAddress(AddressType value) {
        this.shippingAddress = value;
    }
 
}

Тип адреса

Опять же, поскольку классы модели JAXB соответствуют сложным типам, класс создается для сложного типа адресного типа . Поскольку для этого именованного сложного типа могут существовать несколько элементов корневого уровня, это не аннотируется @XmlRootElement .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package org.example.customer;
 
import javax.xml.bind.annotation.*;
 
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "address-type", propOrder = {"street"})
public class AddressType {
 
    @XmlElement(required = true)
    protected String street;
 
    public String getStreet() {
        return street;
    }
 
    public void setStreet(String value) {
        this.street = value;
    }
 
}

ObjectFactory

Аннотация @XmlElementDecl используется для представления корневых элементов, соответствующих именованным сложным типам. Он помещается в метод фабрики в классе, аннотированном @XmlRegistry (при создании из схемы XML этот класс всегда называется ObjectFactory ). Метод фабрики возвращает объект домена, завернутый в экземпляр JAXBElement. JAXBElement имеет QName, который представляет имя элемента и URI пространства имен.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package org.example.customer;
 
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
 
@XmlRegistry
public class ObjectFactory {
 
    private final static QName _BillingAddress_QNAME = new QName("http://www.example.org/customer", "billing-address");
    private final static QName _ShippingAddress_QNAME = new QName("http://www.example.org/customer", "shipping-address");
 
    public ObjectFactory() {
    }
 
    public Customer createCustomer() {
        return new Customer();
    }
 
    public AddressType createAddressType() {
        return new AddressType();
    }
 
    @XmlElementDecl(namespace = "http://www.example.org/customer", name = "billing-address")
    public JAXBElement<AddressType> createBillingAddress(AddressType value) {
        return new JAXBElement<AddressType>(_BillingAddress_QNAME, AddressType.class, null, value);
    }
 
    @XmlElementDecl(namespace = "http://www.example.org/customer", name = "shipping-address")
    public JAXBElement<AddressType> createShippingAddress(AddressType value) {
        return new JAXBElement<AddressType>(_ShippingAddress_QNAME, AddressType.class, null, value);
    }
 
}

Пакет-инфо

Класс package-info используется для определения сопоставления пространства имен (см. JAXB & Namespaces ).

1
2
3
4
@XmlSchema(namespace = "http://www.example.org/customer", elementFormDefault = XmlNsForm.QUALIFIED)
package org.example.customer;
 
import javax.xml.bind.annotation.*;

Unmarshal Operation

Теперь мы рассмотрим влияние типа корневого элемента при демаршаллинге XML.

customer.xml

Ниже приведен пример XML-документа с клиентом в качестве корневого элемента. Помните, что элемент customer имел анонимный сложный тип.

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
    <billing-address>
        <street>1 Any Street</street>
    </billing-address>
    <shipping-address>
        <street>2 Another Road</street>
    </shipping-address>
</customer>

shipping.xml

Вот пример XML-документа с адресом доставки в качестве корневого элемента. Элемент shipping-address имеет названный комплексный тип.

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<shipping-address xmlns="http://www.example.org/customer">
    <street>2 Another Road</street>
</shipping-address>

Демарш Демо
 
При демаршировании XML, который соответствует классу, аннотированному @XmlRootElement, вы получаете экземпляр объекта домена. Но при демаршировании XML, который соответствует классу, аннотированному @XmlElementDecl, вы получаете объект домена, обернутый в экземпляр J AXBElement . В этом примере вам может понадобиться использовать QName из JAXBElement, чтобы определить, не выписали ли вы адрес для выставления счета или адрес доставки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.example.customer;
 
import java.io.File;
import javax.xml.bind.*;
 
public class UnmarshalDemo {
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("org.example.customer");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
 
        // Unmarshal Customer
        File customerXML = new File("src/org/example/customer/customer.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(customerXML);
 
        // Unmarshal Shipping Address
        File shippingXML = new File("src/org/example/customer/shipping.xml");
        JAXBElement<AddressType> je = (JAXBElement<AddressType>) unmarshaller.unmarshal(shippingXML);
        AddressType shipping = je.getValue();
    }
 
}

Демо Unmarshal — JAXBIntrospector
 
Если вы не хотите иметь дело с запоминанием, будет ли результатом операции unmarshal будет объект домена или JAXBElement , то вы можете использовать метод JAXBIntrospector.getValue (Object), чтобы всегда получать объект домена.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example.customer;
 
import java.io.File;
import javax.xml.bind.*;
 
public class JAXBIntrospectorDemo {
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("org.example.customer");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
 
        // Unmarshal Customer
        File customerXML = new File("src/org/example/customer/customer.xml");
        Customer customer = (Customer) JAXBIntrospector.getValue(unmarshaller
                .unmarshal(customerXML));
 
        // Unmarshal Shipping Address
        File shippingXML = new File("src/org/example/customer/shipping.xml");
        AddressType shipping = (AddressType) JAXBIntrospector
                .getValue(unmarshaller.unmarshal(shippingXML));
    }
 
}

Маршал Операция
 
Вы можете напрямую упорядочить объект, аннотированный @XmlRootElement, в XML. Классы, соответствующие аннотациям @XmlElementDecl, должны быть сначала помещены в экземпляр JAXBElement . Фабричный метод, который вы аннотировали @XmlElementDecl, — это самый простой способ сделать это. Метод фабрики находится в классе ObjectFactory, если вы сгенерировали свою модель из схемы XML.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.example.customer;
 
import javax.xml.bind.*;
 
public class MarshalDemo {
 
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("org.example.customer");
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 
        // Create Domain Objects
        AddressType billingAddress = new AddressType();
        billingAddress.setStreet("1 Any Street");
        Customer customer = new Customer();
        customer.setBillingAddress(billingAddress);
 
        // Marshal Customer
        marshaller.marshal(customer, System.out);
 
        // Marshal Billing Address
        ObjectFactory objectFactory = new ObjectFactory();
        JAXBElement<AddressType> je =  objectFactory.createBillingAddress(billingAddress);
        marshaller.marshal(je, System.out);
    }
 
}

Выход

Ниже приведен результат запуска демонстрационного кода.

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <billing-address>
        <street>1 Any Street</street>
    </billing-address>
</customer>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<billing-address xmlns="http://www.example.org/customer">
    <street>1 Any Street</street>
</billing-address>

Ссылка: JAXB и Root Elements от нашего партнера JCG Блэза Дафана в блоге Java XML & JSON Binding .