Unmarshalling-Daten mit JAXB

1. Einleitung

In diesem Tutorial erfahren Sie, wie Sie Datumsobjekte mit verschiedenen Formaten mit JAXB demarshallen.

Zunächst wird das Standard-Datumsformat des Schemas behandelt. Anschließend erfahren Sie, wie Sie verschiedene Formate verwenden. Wir werden auch sehen, wie wir mit einer gemeinsamen Herausforderung umgehen können, die sich aus diesen Techniken ergibt.

2. Schema zu Java-Bindung

Zunächst müssen wir die Beziehung zwischen dem XML-Schema und den Java-Datentypen verstehen . Insbesondere interessiert uns die Zuordnung zwischen einem XML-Schema und Java-Datumsobjekten.

Gemäß der Zuordnung von Schema zu Java müssen drei Schemadatentypen berücksichtigt werden: xsd: date , xsd: time und xsd: dateTime . Wie wir sehen können, sind alle von ihnen javax.xml.datatype.XMLGregorianCalendar zugeordnet .

Wir müssen auch die Standardformate für diese XML-Schematypen verstehen. Die Datentypen xsd: date und xsd: time haben die Formate „ JJJJ-MM-TT“ und „ hh: mm: ss“ . Das Format xsd: dateTime lautet " JJJJ-MM-TTThh: mm: ss", wobei " T" ein Trennzeichen ist, das den Beginn des Zeitabschnitts angibt.

3. Verwenden des Standardschema-Datumsformats

Wir werden ein Beispiel erstellen, das Datumsobjekte aufhebt. Konzentrieren wir uns auf den Datentyp xsd: dateTime , da er eine Obermenge der anderen Typen ist.

Verwenden wir eine einfache XML-Datei, die ein Buch beschreibt:

 Book1 1979-10-21T03:31:12 

Wir möchten die Datei dem entsprechenden Java Book- Objekt zuordnen:

@XmlRootElement(name = "book") public class Book { @XmlElement(name = "title", required = true) private String title; @XmlElement(name = "published", required = true) private XMLGregorianCalendar published; @Override public String toString() { return "[title: " + title + "; published: " + published.toString() + "]"; } }

Schließlich müssen wir eine Clientanwendung erstellen, die die XML-Daten in von JAXB abgeleitete Java-Objekte konvertiert:

public static Book unmarshalDates(InputStream inputFile) throws JAXBException { JAXBContext jaxbContext = JAXBContext.newInstance(Book.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); return (Book) jaxbUnmarshaller.unmarshal(inputFile); }

Im obigen Code haben wir einen JAXBContext definiert, der der Einstiegspunkt in die JAXB-API ist. Dann haben wir einen JAXB Unmarshaller in einem Eingabestream verwendet, um unser Objekt zu lesen:

Wenn wir den obigen Code und drucken Sie das Ergebnis ausführen, werden wir folgendes erhalten Buchobjekt:

[title: Book1; published: 1979-11-28T02:31:32]

Wir sollten beachten, dass wir, obwohl die Standardzuordnung für xsd: dateTime der XMLGregorianCalendar ist , laut JAXB-Benutzerhandbuch auch die gebräuchlicheren Java-Typen java.util.Date und java.util.Calendar hätten verwenden können .

4. Verwenden eines benutzerdefinierten Datumsformats

Das obige Beispiel funktioniert, weil wir das Standardschema-Datumsformat "JJJJ-MM-TTThh: mm: ss" verwenden.

Aber was ist, wenn wir ein anderes Format wie "JJJJ-MM-TT hh: mm: ss" verwenden möchten , um das Trennzeichen "T" zu entfernen? Wenn wir das Trennzeichen durch ein Leerzeichen in unserer XML-Datei ersetzen würden, würde das Standard-Unmarshalling fehlschlagen.

4.1. Erstellen eines benutzerdefinierten XmlAdapters

Um ein anderes Datumsformat zu verwenden, müssen wir einen XmlAdapter definieren .

Lassen Sie uns auch sehen, wie Sie den Typ xsd: dateTime mit unserem benutzerdefinierten XmlAdapter einem java.util.Date- Objekt zuordnen:

public class DateAdapter extends XmlAdapter { private static final String CUSTOM_FORMAT_STRING = "yyyy-MM-dd HH:mm:ss"; @Override public String marshal(Date v) { return new SimpleDateFormat(CUSTOM_FORMAT_STRING).format(v); } @Override public Date unmarshal(String v) throws ParseException { return new SimpleDateFormat(CUSTOM_FORMAT_STRING).parse(v); } }

In diesem Adapter haben wir SimpleDateFormat verwendet, um unser Datum zu formatieren. Wir müssen vorsichtig sein , da die Simple nicht Thread - sicher-ist. Um zu vermeiden, dass bei mehreren Threads Probleme mit einem freigegebenen SimpleDateFormat- Objekt auftreten, erstellen wir jedes Mal ein neues, wenn wir es benötigen.

4.2. Die Interna des XmlAdapters

Wie wir sehen können, verfügt der XmlAdapter über zwei Typparameter , in diesem Fall String und Date . Der erste Typ ist der im XML verwendete Typ und wird als Werttyp bezeichnet. In diesem Fall kann JAXB einen XML-Wert in einen String konvertieren . Der zweite wird als gebundener Typ bezeichnet und bezieht sich auf den Wert in unserem Java-Objekt.

Das Ziel eines Adapters besteht darin, zwischen dem Werttyp und einem gebundenen Typ auf eine Weise zu konvertieren, die JAXB standardmäßig nicht kann.

Um einen benutzerdefinierten XML- Adapter zu erstellen , müssen zwei Methoden überschrieben werden: XmlAdapter.marshal () und XmlAdapter.unmarshal () .

Während des Aufhebens der Marshalling-Funktion löst das JAXB-Bindungsframework zuerst die XML-Darstellung in einem String auf und ruft dann DateAdapter.unmarshal () auf, um den Werttyp an ein Datum anzupassen . Während des Marshalling ruft das JAXB-Bindungsframework DateAdapter.marshal () auf, um ein Datum an einen String anzupassen , der dann in einer XML-Darstellung gemarshallt wird.

4.3. Integration über die JAXB Annotations

Der DateAdapter funktioniert wie ein Plugin für JAXB und wird mit der Annotation @XmlJavaTypeAdapter an unser Datumsfeld angehängt . Die Annotation @XmlJavaTypeAdapte r gibt die Verwendung eines XmlAdapter für das benutzerdefinierte Unmarshalling an :

@XmlRootElement(name = "book") public class BookDateAdapter { // same as before @XmlElement(name = "published", required = true) @XmlJavaTypeAdapter(DateAdapter.class) private Date published; // same as before }

Wir verwenden auch die Standard-JAXB-Annotationen: @XmlRootElement- und @XmlElement- Annotationen.

Lassen Sie uns zum Schluss den neuen Code ausführen:

[title: Book1; published: Wed Nov 28 02:31:32 EET 1979]

5. Unmarshalling-Daten in Java 8

Java 8 hat eine neue Datums- / Uhrzeit- API eingeführt. Hier konzentrieren wir uns auf die LocalDateTime- Klasse, die eine der am häufigsten verwendeten ist.

5.1. Erstellen eines LocalDateTime- basierten XmlAdapters

Standardmäßig kann JAXB einen xsd: dateTime- Wert unabhängig vom Datumsformat nicht automatisch an ein LocalDateTime- Objekt binden . Um einen XML- Schemadatumswert in oder von einem LocalDateTime- Objekt zu konvertieren , müssen Sie einen anderen XmlAdapter definieren , der dem vorherigen ähnlich ist:

public class LocalDateTimeAdapter extends XmlAdapter { private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Override public String marshal(LocalDateTime dateTime) { return dateTime.format(dateFormat); } @Override public LocalDateTime unmarshal(String dateTime) { return LocalDateTime.parse(dateTime, dateFormat); } }

In diesem Fall haben wir einen gebrauchten DateTimeFormatter anstelle eines Simple . Ersteres wurde in Java 8 eingeführt und ist mit der neuen Datums- / Uhrzeit- API kompatibel .

Beachten Sie, dass die Umwandlungsoperationen ein gemeinsam nutzen können DateTimeFormatter Objekt , weil die DateTimeFormatter Thread-sicher ist.

5.2. Integration des neuen Adapters

Nun wollen sie den alten Adapter mit dem neuen in unserer ersetzen Buch Klasse und auch Datum mit Local :

@XmlRootElement(name = "book") public class BookLocalDateTimeAdapter { // same as before @XmlElement(name = "published", required = true) @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) private LocalDateTime published; // same as before }

Wenn wir den obigen Code ausführen, erhalten wir die Ausgabe:

[title: Book1; published: 1979-11-28T02:31:32]

Beachten Sie, dass LocalDateTime.toString () das Trennzeichen "T" zwischen Datum und Uhrzeit hinzufügt .

6. Fazit

In diesem Tutorial haben wir uns mit JAXB mit dem Sammeln von Daten befasst .

Zuerst haben wir uns die Zuordnung des XML-Schemas zum Java-Datentyp angesehen und ein Beispiel mit dem Standard-Datumsformat des XML-Schemas erstellt.

Als Nächstes haben wir gelernt, wie ein benutzerdefiniertes Datumsformat basierend auf einem benutzerdefinierten XmlAdapter verwendet wird, und wie die Thread-Sicherheit von SimpleDateFormat behandelt wird .

Schließlich nutzten wir die überlegene, threadsichere Java 8-Datums- / Zeit-API und nicht gemarshallte Daten mit benutzerdefinierten Formaten.

Wie immer ist der im Tutorial verwendete Quellcode über GitHub verfügbar.