В предыдущих постах я рассмотрел, как отобразить отношения наследования в JAXB. Это можно сделать по имени элемента (через @XmlElementRef), по атрибуту xsi: type или в EclipseLink MOXy, используя другой атрибут XML (через @ XmlDescriminatorNode / @ XmlDescriminatorValue). В этом посте индикатор типа будет атрибутом / элементом XML, уникальным для этого типа, и мы будем использовать XmlAdapter для реализации этого поведения.
В этом примере возможными методами контакта являются
Address и
PhoneNumber . Если
атрибут street присутствует в
элементе contact-method, мы будем создавать экземпляр
объекта Address , а если
указан атрибут number, мы
создаем объект PhoneNumber .
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<contact-method
number="555-1111"/>
<contact-method
street="1 A St"
city = "Any Town"/>
<contact-method
number="555-2222"/>
</customer>
Ниже приведена модель домена, которая будет использоваться в этом примере.
package blog.inheritance.xmladapter;
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElement(name="contact-method")
private List<ContactMethod> contactMethods;
}
ContactMethod и его подклассы (
Address &
PhoneNumber) будут обрабатываться
XmlAdapter , поэтому единственное требуемое сопоставление — это
@XmlJavaTypeAdapter, чтобы указать реализацию
XmlAdapter .
package blog.inheritance.xmladapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlJavaTypeAdapter(ContactMethodAdapter.class)
public abstract class ContactMethod {
}
package blog.inheritance.xmladapter; public class Address extends ContactMethod { protected String street; protected String city; }
package blog.inheritance.xmladapter; public class PhoneNumber extends ContactMethod { protected String number; }
Класс AdaptedContactMethod создан и представляет объединенные свойства ContactMethod , Address и PhoneNumber . В маршальной операции заполняются только свойства, соответствующие маршалируемому типу. Во время операции unmarshal после того, как AdaptedContactMethod был построен, мы можем посмотреть, какие свойства были заполнены, чтобы определить соответствующий подтип, который должен быть возвращен.
package blog.inheritance.xmladapter; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.adapters.XmlAdapter; public class ContactMethodAdapter extends XmlAdapter<ContactMethodAdapter.AdaptedContactMethod, ContactMethod> { @Override public AdaptedContactMethod marshal(ContactMethod contactMethod) throws Exception { if (null == contactMethod) { return null; } AdaptedContactMethod adaptedContactMethod = new AdaptedContactMethod(); if (contactMethod instanceof Address) { Address address = (Address) contactMethod; adaptedContactMethod.street = address.street; adaptedContactMethod.city = address.city; } else { PhoneNumber phoneNumber = (PhoneNumber) contactMethod; adaptedContactMethod.number = phoneNumber.number; } return adaptedContactMethod; } @Override public ContactMethod unmarshal(AdaptedContactMethod adaptedContactMethod) throws Exception { if (null == adaptedContactMethod) { return null; } if (null != adaptedContactMethod.number) { PhoneNumber phoneNumber = new PhoneNumber(); phoneNumber.number = adaptedContactMethod.number; return phoneNumber; } else { Address address = new Address(); address.street = adaptedContactMethod.street; address.city = adaptedContactMethod.city; return address; } } public static class AdaptedContactMethod { @XmlAttribute public String number; @XmlAttribute public String street; @XmlAttribute public String city; } }
Следующий демонстрационный код будет использоваться для этого примера. Мы демонтируем входной документ, выводим тип каждого объекта в коллекции, а затем перенаправляем объекты обратно в XML.
package blog.inheritance.xmladapter; import java.io.File; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Customer.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/blog/inheritance/xmladapter/input.xml"); Customer customer = (Customer) unmarshaller.unmarshal(xml); for(ContactMethod contactMethod : customer.getContactMethods()) { System.out.println(contactMethod.getClass()); } Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(customer, System.out); } }
Ниже приведен результат запуска демонстрационного кода. Обратите внимание, что каждый из экземпляров ContactMethod в коллекции имеет соответствующий подтип.
class blog.inheritance.xmladapter.PhoneNumber class blog.inheritance.xmladapter.Address class blog.inheritance.xmladapter.PhoneNumber <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <customer> <contact-method number="555-1111"/> <contact-method city="Any Town" street="1 A St"/> <contact-method number="555-2222"/> </customer>
- eclipselink / Moxy: наследование и перегрузка имени атрибута в зависимости от типа
- Jaxb объекты с тем же именем
- Java / JAXB: Unmarshal атрибуты XML для определенных атрибутов объекта Java
От http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html