¿Puede JAXB dirigir por contención al principio y luego mariscal por @XmlIDREF para referencias posteriores?

Me pregunto si es posible hacer anotaciones en mis clases para que la primera vez que el Marshaller encuentre un objeto, genere un elemento XML del tipo apropiado, pero cualquier otra referencia posterior a este objeto tendrá una entrada XML IDREF creada.

Puede aprovechar el concepto de XmlAdapter de JAXB para hacer algo como lo siguiente:

input.xml

El siguiente es el documento XML que usaré para este ejemplo. La tercera entrada de phone-number es una referencia a la primera entrada de phone-number , y la 5ta entrada de phone-number es una referencia a la 4a.

 < ?xml version="1.0" encoding="UTF-8" standalone="yes"?>   555-AAAA   555-BBBB    555-WORK 1234    

Cliente

La clase de cliente mantiene una colección de objetos PhoneNumber . La misma instancia de PhoneNumber puede aparecer varias veces en la colección.

 package forum7587095; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Customer { private List phoneNumbers; @XmlElement(name="phone-number") public List getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(List phoneNumbers) { this.phoneNumbers = phoneNumbers; } } 

Número de teléfono

Esta es una clase que puede aparecer en el documento mismo o como referencia. Esto se manejará usando un XmlAdapter . Un XmlAdapter se configura utilizando la anotación @XmlJavaTypeAdapter . Como hemos especificado este adaptador en el nivel de tipo / clase, se aplicará a todas las propiedades que hacen referencia a la clase PhoneNumber :

 package forum7587095; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlJavaTypeAdapter(PhoneNumberAdapter.class) public class PhoneNumber { private String id; private String number; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public boolean equals(Object arg0) { if(null == arg0 || arg0.getClass() != this.getClass()) { return false; } PhoneNumber test = (PhoneNumber) arg0; if(!equals(id, test.getId())) { return false; } return equals(number, test.getNumber()); } protected boolean equals(String control, String test) { if(null == control) { return null == test; } else { return control.equals(test); } } @Override public int hashCode() { return id.hashCode(); } } 

Número de teléfono del trabajo

En base a su comentario, he agregado una subclase de PhoneNumber .

 package forum7587095; public class WorkPhoneNumber extends PhoneNumber { private String extension; public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } @Override public boolean equals(Object arg0) { if(!super.equals(arg0)) { return false; } return equals(extension, ((WorkPhoneNumber) arg0).getExtension()); } } 

PhoneNumberAdapter

A continuación se muestra la implementación de XmlAdapter . Tenga en cuenta que debemos mantener si el objeto PhoneNumber se ha visto antes. Si tiene, solo AdaptedPhoneNumber la porción de id del objeto AdaptedPhoneNumber .

 package forum7587095; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlAdapter; public class PhoneNumberAdapter extends XmlAdapter{ private List phoneNumberList = new ArrayList(); private Map phoneNumberMap = new HashMap(); @XmlSeeAlso(AdaptedWorkPhoneNumber.class) @XmlType(name="phone-number") public static class AdaptedPhoneNumber { @XmlAttribute public String id; public String number; public AdaptedPhoneNumber() { } public AdaptedPhoneNumber(PhoneNumber phoneNumber) { id = phoneNumber.getId(); number = phoneNumber.getNumber(); } public PhoneNumber getPhoneNumber() { PhoneNumber phoneNumber = new PhoneNumber(); phoneNumber.setId(id); phoneNumber.setNumber(number); return phoneNumber; } } @XmlType(name="work-phone-number") public static class AdaptedWorkPhoneNumber extends AdaptedPhoneNumber { public String extension; public AdaptedWorkPhoneNumber() { } public AdaptedWorkPhoneNumber(WorkPhoneNumber workPhoneNumber) { super(workPhoneNumber); extension = workPhoneNumber.getExtension(); } @Override public WorkPhoneNumber getPhoneNumber() { WorkPhoneNumber phoneNumber = new WorkPhoneNumber(); phoneNumber.setId(id); phoneNumber.setNumber(number); phoneNumber.setExtension(extension); return phoneNumber; } } @Override public AdaptedPhoneNumber marshal(PhoneNumber phoneNumber) throws Exception { AdaptedPhoneNumber adaptedPhoneNumber; if(phoneNumberList.contains(phoneNumber)) { if(phoneNumber instanceof WorkPhoneNumber) { adaptedPhoneNumber = new AdaptedWorkPhoneNumber(); } else { adaptedPhoneNumber = new AdaptedPhoneNumber(); } adaptedPhoneNumber.id = phoneNumber.getId(); } else { if(phoneNumber instanceof WorkPhoneNumber) { adaptedPhoneNumber = new AdaptedWorkPhoneNumber((WorkPhoneNumber)phoneNumber); } else { adaptedPhoneNumber = new AdaptedPhoneNumber(phoneNumber); } phoneNumberList.add(phoneNumber); } return adaptedPhoneNumber; } @Override public PhoneNumber unmarshal(AdaptedPhoneNumber adaptedPhoneNumber) throws Exception { PhoneNumber phoneNumber = phoneNumberMap.get(adaptedPhoneNumber.id); if(null != phoneNumber) { return phoneNumber; } phoneNumber = adaptedPhoneNumber.getPhoneNumber(); phoneNumberMap.put(phoneNumber.getId(), phoneNumber); return phoneNumber; } } 

Manifestación

Para garantizar que se utiliza la misma instancia de XmlAdapter para unmarshal operaciones de unmarshal y unmarshal , debemos establecer específicamente una instancia de XmlAdapter tanto en Marshaller como en Unmarshaller :

 package forum7587095; import java.io.File; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Customer.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setAdapter(new PhoneNumberAdapter()); File xml = new File("src/forum7587095/input.xml"); Customer customer = (Customer) unmarshaller.unmarshal(xml); System.out.println(customer.getPhoneNumbers().get(0) == customer.getPhoneNumbers().get(2)); System.out.println(customer.getPhoneNumbers().get(3) == customer.getPhoneNumbers().get(4)); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setAdapter(new PhoneNumberAdapter()); marshaller.marshal(customer, System.out); } } 

Salida

 true true < ?xml version="1.0" encoding="UTF-8" standalone="yes"?>   555-AAAA   555-BBBB    555-WORK 1234    

Para más información