import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElementRefs({
@XmlElementRef(name = "billing-address"),
@XmlElementRef(name = "shipping-address")
})
private JAXBElement<Address> address;
}
Полезный JAXBElement может помешать, если вы хотите использовать модель вашего домена с чем-то вроде JPA (который не знает, что с этим делать). В этом посте я продемонстрирую, как можно устранить необходимость в JAXBElement с помощью XmlAdapter.
Адрес
Имя элемента и информация о пространстве имен из JAXBElement должны где-то храниться. Вместо использования JAXBElement мы установим имя элемента и пространство имен в свойстве QName объекта Address (строка 10). Поскольку мы не будем отображать поле qName, оно было помечено @XmlTransient (строка 9, см. JAXB и Unmapped Properties ).
package blog.jaxbelement.remove;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
@XmlTransient
private QName qName;
private String street;
private String city;
public QName getQName() {
return qName;
}
public void setQName(QName name) {
this.qName = name;
}
}
AddressAdapter
Мы будем использовать XmlAdapter для преобразования экземпляра Address в / из экземпляра JAXBElement . Во время этого преобразования мы должны переместить имя и информацию о пространстве имен между Address и JAXBElement .
package blog.jaxbelement.remove;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class AddressAdapter extends XmlAdapter<JAXBElement<Address>, Address>{
@Override
public JAXBElement<Address> marshal(Address address) throws Exception {
return new JAXBElement<Address>(address.getQName(), Address.class, address);
}
@Override
public Address unmarshal(JAXBElement<Address> jaxbElement) throws Exception {
Address address = jaxbElement.getValue();
address.setQName(jaxbElement.getName());
return address;
}
}
Заказчик
@XmlJavaTypeAdapter аннотации используются для указания XMLAdapter . XMLAdapter отвечает за преобразование экземпляра адреса в JAXBElement , чтобы удовлетворить потребности @XmlElementRefs отображения. Название собственность на @XmlElementRef аннотации должна соответствовать имени , указанному в @XmlRootElement или @XmlElementDecl аннотации.
package blog.jaxbelement.remove;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElementRefs({
@XmlElementRef(name = "billing-address"),
@XmlElementRef(name = "shipping-address")
})
@XmlJavaTypeAdapter(AddressAdapter.class)
private Address address;
public Address getAddress() {
return address;
}
}
ObjectFactory
@XmlElementDecl аннотации используются , когда класс связан с несколькими элементами (если класс связан только с одним элементом тогда @XmlRootElement может быть использовано). Он помещается в метод фабрики в классе, аннотированном @XmlRegistry (при создании из схемы XML этот класс всегда называется ObjectFactory ). Метод фабрики возвращает объект домена, завернутый в экземпляр JAXBElement. JAXBElement имеет QName , представляющее имя элементов и URI пространства имен.
package blog.jaxbelement.remove;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
static final String BILLING_ADDRESS = "billing-address";
static final String SHIPPING_ADDRESS = "shipping-address";
@XmlElementDecl(name=BILLING_ADDRESS)
public JAXBElement<Address> createBillingAddress(Address address) {
return new JAXBElement<Address>(new QName(BILLING_ADDRESS), Address.class, address);
}
@XmlElementDecl(name=SHIPPING_ADDRESS)
public JAXBElement<Address> createShippingAddress(Address address) {
return new JAXBElement<Address>(new QName(SHIPPING_ADDRESS), Address.class, address);
}
}
input.xml
Ниже приведен ввод демо-кода. Обратите внимание, как адресные данные помещаются в элемент billing-address .
Элемент billing-address был одним из имен элементов, которые мы указали в аннотации @XmlElementRef в классе Customer . В демонстрационном коде мы заменим это на элемент shipping-address , имя другого элемента, которое мы указали в аннотации @XmlElementRef .
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<billing-address>
<street>123 A Street</street>
<city>Any Town</city>
</billing-address>
</customer>
Демо
В демо коде ниже мы будем:
- Демонстрация входного документа (строка 14)
- Установите новое QName для объекта Address . QName должен соответствовать одному из @XmlElementDecl аннотаций на ObjectFactory класса (строка 17).
- Маршал объекта Customer возвращается в XML (строка 21).
package blog.jaxbelement.remove;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class,ObjectFactory.class);
Unmarshaller u = jc.createUnmarshaller();
File xml = new File("src/blog/jaxbelement/remove/input.xml");
Customer customer = (Customer) u.unmarshal(xml);
// Change the Wrapper Element
customer.getAddress().setQName(new QName(ObjectFactory.SHIPPING_ADDRESS));
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
}
}
Выходные данные
Ниже приведен результат запуска демонстрационного кода. Обратите внимание, как данные адреса теперь обернуты в элемент shipping-address .
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<shipping-address>
<street>123 A Street</street>
<city>Any Town</city>
</shipping-address>
</customer>