Zastanawiam się, czy możliwe jest dodawanie adnotacji do moich klas, tak aby po raz pierwszy, gdy marshaller napotka obiekt, generuje element XML odpowiedniego typu, ale każde kolejne odwołanie do tego obiektu przez cokolwiek innego będzie miało wpis XML IDREF stworzony?Czy JAXB może najpierw bronić się przed powstrzymywaniem, a następnie przesyłać dalej przez @XmlIDREF w celu uzyskania kolejnych referencji?
Odpowiedz
można wykorzystać koncepcję JAXB na XmlAdapter
zrobić coś jak następuje:
input.xml
Poniżej znajduje się dokument XML Użyję do tego przykładu. The 3rd phone-number
wpis jest nawiązaniem do 1 phone-number
wpisu, a 5th phone-number
wpis jest nawiązaniem do 4 .:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<phone-number id="A">
<number>555-AAAA</number>
</phone-number>
<phone-number id="B">
<number>555-BBBB</number>
</phone-number>
<phone-number id="A"/>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
<number>555-WORK</number>
<extension>1234</extension>
</phone-number>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>
klientów
Klasa klient utrzymuje kolekcję PhoneNumber
obiektów. To samo wystąpienie numeru PhoneNumber może pojawić się wiele razy w kolekcji.
package forum7587095;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Customer {
private List<PhoneNumber> phoneNumbers;
@XmlElement(name="phone-number")
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
PhoneNumber
Jest to klasa, która może pojawić się zarówno w samym dokumencie lub jako odniesienie. Będzie to obsługiwane przy użyciu XmlAdapter
. XmlAdapter jest skonfigurowany przy użyciu adnotacji @XmlJavaTypeAdapter
. Ponieważ mamy określony adaptera na poziomie typu/klasy będzie ona miała zastosowanie do wszystkich właściwości odwołujących klasę 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();
}
}
WorkPhoneNumber
Oparte na Twój komentarz dodałem podklasę 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
Poniżej jest realizacja XmlAdapter
. Zauważ, że musimy zachować, jeśli obiekt PhoneNumber był wcześniej widziany. Jeśli tak jest, wypełniamy tylko część obiektu id
obiektu .
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<PhoneNumberAdapter.AdaptedPhoneNumber, PhoneNumber>{
private List<PhoneNumber> phoneNumberList = new ArrayList<PhoneNumber>();
private Map<String, PhoneNumber> phoneNumberMap = new HashMap<String, PhoneNumber>();
@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;
}
}
Demo
Aby zapewnić tę samą instancję XmlAdapter
służy do całych marshal
i unmarshal
operacji musimy szczegółowo określone wystąpienie XmlAdapter zarówno na Marshaller
i 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);
}
}
Wyjście
true
true
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<phone-number id="A">
<number>555-AAAA</number>
</phone-number>
<phone-number id="B">
<number>555-BBBB</number>
</phone-number>
<phone-number id="A"/>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
<number>555-WORK</number>
<extension>1234</extension>
</phone-number>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>
Aby uzyskać więcej informacji
- http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html
- http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
- http://blog.bdoughan.com/search/label/XmlAdapter
- http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
- http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
- 1. Jak się bronić przed TabNabbing?
- 2. Dlaczego najpierw czytać plik, a następnie sprawdzić?
- 3. Czy warto najpierw nauczyć się LINQ, a następnie SQL?
- 4. Czy można zmienić kolejność kart/fokusów w tabeli html, aby przesyłać je najpierw w pionie, a następnie w poziomie?
- 5. Słownik bez zliczania referencji w celu-c
- 6. sql, kolejność według kolumny A, a następnie przez kolumny B
- 7. Sortowanie JavaPairRDD najpierw według wartości, a następnie według klucza
- 8. Czy wydajność może generować wiele kolejnych generatorów?
- 9. ORDER BY DATE pokazuje najpierw NULLS, a następnie najnowsze daty.
- 10. Czy muszę najpierw uzyskać Długość listy, a następnie listę zapytań w Redis?
- 11. seek(), a następnie read(), a następnie napisać() w Pythonie
- 12. , jeśli wątek A rozpoczyna się przed wątkiem B w języku Java, to A zostanie zaplanowane przez os przed B?
- 13. Solr/Lucene czy można najpierw zamówić według istotności, a następnie według drugiego atrybutu?
- 14. Czy mogę najpierw WYBRAĆ, a następnie USUWAĆ rekordy w jednej transakcji t-SQL?
- 15. laravel sortowania kolekcji, a następnie przez kluczowego
- 16. Nieznany dostawca: $ rootElementProvider podczas korzystania z wtryskiwacza $ $ w celu uzyskania usługi $ location przed ustawieniem kątowym.bootstrap
- 17. Czy możliwe jest użycie opcji "Zaloguj się przez PayPal" w celu uzyskania żądań REST api w imieniu użytkownika?
- 18. Czy mogę napisać Iterator, który sam się mutuje, a następnie zwraca się do siebie?
- 19. Przerzucanie obrazu w celu uzyskania efektu lustra
- 20. Angular JS - żądanie w celu uzyskania obrazu
- 21. Uzyskiwanie elementu event.target w celu uzyskania elementu
- 22. Grupa wzorów/Matchera() w celu uzyskania podciągu w języku Java?
- 23. Autoryzacja komputera w celu uzyskania dostępu do aplikacji internetowej
- 24. Sposób korzystania z LINQ w celu uzyskania wielu podsumowań
- 25. C# winform usuwanie, a następnie dodawanie kolejnych elementów do panelu sterowania
- 26. odczytywanie i analizowanie pliku TSV, a następnie manipulowanie nim w celu zapisania jako pliku CSV (* wydajnie *)
- 27. first_or_create przez e-mail, a następnie zapisać zagnieżdżonego modelowi
- 28. Obejście w celu uzyskania właściwości chronionych w Objective-C
- 29. Czy skrypt bash może uruchamiać jednoczesne polecenia, a następnie czekać na ich zakończenie?
- 30. IF EXISTS, a następnie wybierz ELSE INSERT, a następnie wybierz
Dzięki Blaise za bardzo szczegółową odpowiedź! Czy to pojęcie jest takie samo, jeśli "PhoneNumber" ma podklasy? Na przykład "Klient" ma wiele odniesień do tej samej instancji podklasy 'Numer_telefonu'? – holic87
@ holic87 - Zaktualizowałem swoją odpowiedź, aby 'PhoneNumber' miał podklasy. –
Dzięki za wyjaśnienie! – holic87