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>