XStream-Benutzerhandbuch: Konvertieren von Objekten in XML

1. Übersicht

In diesem Tutorial erfahren Sie, wie Sie mithilfe der XStream-Bibliothek Java-Objekte in XML serialisieren.

2. Funktionen

Die Verwendung von XStream zum Serialisieren und Deserialisieren von XML bietet einige interessante Vorteile:

  • Richtig konfiguriert, erzeugt es sehr sauberes XML
  • Bietet wichtige Möglichkeiten zur Anpassung der XML-Ausgabe
  • Unterstützung für Objektdiagramme , einschließlich Zirkelverweisen
  • In den meisten Anwendungsfällen ist die XStream-Instanz nach der Konfiguration threadsicher (bei der Verwendung von Anmerkungen gibt es Einschränkungen).
  • Während der Ausnahmebehandlung werden eindeutige Meldungen bereitgestellt , um die Diagnose von Problemen zu erleichtern
  • Ab Version 1.4.7 stehen Sicherheitsfunktionen zur Verfügung, mit denen die Serialisierung bestimmter Typen nicht zugelassen werden kann

3. Projekteinrichtung

Um XStream in unserem Projekt verwenden zu können, fügen wir die folgende Maven-Abhängigkeit hinzu:

 com.thoughtworks.xstream xstream 1.4.9 

4. Grundlegende Verwendung

Die XStream- Klasse ist eine Fassade für die API. Beim Erstellen einer Instanz von XStream müssen wir uns auch um Thread-Sicherheitsprobleme kümmern:

XStream xstream = new XStream();

Sobald eine Instanz erstellt und konfiguriert wurde, kann sie für das Marshalling / Unmarshalling von mehreren Threads gemeinsam genutzt werden, es sei denn, Sie aktivieren die Annotationsverarbeitung.

4.1. Treiber

Es werden mehrere Treiber unterstützt, z. B. DomDriver , StaxDriver , XppDriver und mehr. Diese Treiber weisen unterschiedliche Leistungs- und Ressourcennutzungsmerkmale auf.

Der XPP3-Treiber wird standardmäßig verwendet, aber natürlich können wir den Treiber leicht ändern:

XStream xstream = new XStream(new StaxDriver()); 

4.2. XML generieren

Beginnen wir mit der Definition eines einfachen POJO für - Kunde :

public class Customer { private String firstName; private String lastName; private Date dob; // standard constructor, setters, and getters }

Lassen Sie uns nun eine XML-Darstellung des Objekts generieren:

Customer customer = new Customer("John", "Doe", new Date()); String dataXml = xstream.toXML(customer);

Mit den Standardeinstellungen wird die folgende Ausgabe erzeugt:

 John Doe 1986-02-14 03:46:16.381 UTC  

Von diesem Ausgang kann man deutlich sehen , dass das mit Etikett der vollständig qualifizierten Klassennamen verwendet Kunden standardmäßig .

Es gibt viele Gründe, warum wir möglicherweise entscheiden, dass das Standardverhalten nicht unseren Anforderungen entspricht. Beispielsweise ist es uns möglicherweise nicht angenehm, die Paketstruktur unserer Anwendung verfügbar zu machen. Außerdem ist das generierte XML erheblich länger.

5. Aliase

Ein Alias ist ein Name, den wir für Elemente verwenden möchten, anstatt Standardnamen zu verwenden.

Beispielsweise können wir com.baeldung.pojo.Customer durch customer ersetzen, indem wir einen Alias ​​für die Customer- Klasse registrieren . Wir können auch Aliase für Eigenschaften einer Klasse hinzufügen. Durch die Verwendung von Aliasen können wir unsere XML-Ausgabe viel lesbarer und weniger Java-spezifisch machen.

5.1. Klasse Aliase

Aliase können entweder programmgesteuert oder mithilfe von Anmerkungen registriert werden.

Lassen Sie uns jetzt unsere Anmerkungen versehen Kundenklasse mit @XStreamAlias :

@XStreamAlias("customer")

Jetzt müssen wir unsere Instanz so konfigurieren, dass diese Annotation verwendet wird:

xstream.processAnnotations(Customer.class);

Wenn Sie alternativ einen Alias ​​programmgesteuert konfigurieren möchten, können Sie den folgenden Code verwenden:

xstream.alias("customer", Customer.class);

Unabhängig davon, ob der Alias ​​oder die programmgesteuerte Konfiguration verwendet wird, ist die Ausgabe für ein Kundenobjekt viel sauberer:

 John Doe 1986-02-14 03:46:16.381 UTC  

5.2. Feld Aliase

Wir können auch Aliase für Felder hinzufügen, indem wir dieselbe Annotation verwenden, die für Aliasing-Klassen verwendet wird. Wenn wir beispielsweise möchten , dass das Feld firstName in der XML-Darstellung durch fn ersetzt wird , können wir die folgende Anmerkung verwenden:

@XStreamAlias("fn") private String firstName;

Alternativ können wir das gleiche Ziel programmatisch erreichen:

xstream.aliasField("fn", Customer.class, "firstName");

Die aliasField- Methode akzeptiert drei Argumente: den Alias, den wir verwenden möchten, die Klasse, in der die Eigenschaft definiert ist, und den Eigenschaftsnamen, den wir als Alias ​​verwenden möchten.

Unabhängig von der verwendeten Methode ist die Ausgabe dieselbe:

 John Doe 1986-02-14 03:46:16.381 UTC 

5.3. Standard-Aliase

Es gibt mehrere Aliase, die für Klassen vorregistriert sind - hier einige davon:

alias("float", Float.class); alias("date", Date.class); alias("gregorian-calendar", Calendar.class); alias("url", URL.class); alias("list", List.class); alias("locale", Locale.class); alias("currency", Currency.class);

6. Sammlungen

Jetzt fügen wir eine Liste von ContactDetails innerhalb der Kundenklasse hinzu .

private List contactDetailsList;

With default settings for collection handling, this is the output:

 John Doe 1986-02-14 04:14:05.874 UTC   6673543265 0124-2460311   4676543565 0120-223312   

Let's suppose we need to omit the contactDetailsList parent tags, and we just want each ContactDetails element to be a child of the customer element. Let us modify our example again:

xstream.addImplicitCollection(Customer.class, "contactDetailsList");

Now, when the XML is generated, the root tags are omitted, resulting in the XML below:

 John Doe 1986-02-14 04:14:20.541 UTC  6673543265 0124-2460311   4676543565 0120-223312  

The same can also be achieved using annotations:

@XStreamImplicit private List contactDetailsList;

7. Converters

XStream uses a map of Converter instances, each with its own conversion strategy. These convert supplied data to a particular format in XML and back again.

In addition to using the default converters, we can modify the defaults or register custom converters.

7.1. Modifying an Existing Converter

Suppose we weren't happy with the way the dob tags were generatedusing the default settings. We can modify the custom converter for Date provided by XStream (DateConverter):

xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));

The above will produce the output in “dd-MM-yyyy” format:

 John Doe 14-02-1986 

7.2. Custom Converters

We can also create a custom converter to accomplish the same output as in the previous section:

public class MyDateConverter implements Converter { private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); @Override public boolean canConvert(Class clazz) { return Date.class.isAssignableFrom(clazz); } @Override public void marshal( Object value, HierarchicalStreamWriter writer, MarshallingContext arg2) { Date date = (Date)value; writer.setValue(formatter.format(date)); } // other methods }

Finally, we register our MyDateConverter class as below:

xstream.registerConverter(new MyDateConverter());

We can also create converters that implement the SingleValueConverter interface, which is designed to convert an object into a string.

public class MySingleValueConverter implements SingleValueConverter { @Override public boolean canConvert(Class clazz) { return Customer.class.isAssignableFrom(clazz); } @Override public String toString(Object obj) { SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); Date date = ((Customer) obj).getDob(); return ((Customer) obj).getFirstName() + "," + ((Customer) obj).getLastName() + "," + formatter.format(date); } // other methods }

Finally, we register MySingleValueConverter:

xstream.registerConverter(new MySingleValueConverter()); 

Using MySingleValueConverter, the XML output for a Customer is as follows:

John,Doe,14-02-1986

7.3. Converter Priority

When registering Converter objects, is is possible to set their priority level, as well.

From the XStream javadocs:

The converters can be registered with an explicit priority. By default they are registered with XStream.PRIORITY_NORMAL. Converters of same priority will be used in the reverse sequence they have been registered. The default converter, i.e. the converter which will be used if no other registered converter is suitable, can be registered with priority XStream.PRIORITY_VERY_LOW. XStream uses by default the ReflectionConverter as the fallback converter.

The API provides several named priority values:

private static final int PRIORITY_NORMAL = 0; private static final int PRIORITY_LOW = -10; private static final int PRIORITY_VERY_LOW = -20; 

8.Omitting Fields

We can omit fields from our generated XML using either annotations or programmatic configuration. In order to omit a field using an annotation, we simply apply the @XStreamOmitField annotation to the field in question:

@XStreamOmitField private String firstName;

In order to omit the field programmatically, we use the following method:

xstream.omitField(Customer.class, "firstName");

Whichever method we select, the output is the same:

 Doe 14-02-1986 

9. Attribute Fields

Sometimes we may wish to serialize a field as an attribute of an element rather than as element itself. Suppose we add a contactType field:

private String contactType;

If we want to set contactType as an XML attribute, we can use the @XStreamAsAttribute annotation:

@XStreamAsAttribute private String contactType; 

Alternatively, we can accomplish the same goal programmatically:

xstream.useAttributeFor(ContactDetails.class, "contactType");

The output of either of the above methods is the same:

 6673543265 0124-2460311 

10. Concurrency

XStream's processing model presents some challenges. Once the instance is configured, it is thread-safe.

It is important to note that processing of annotations modifies the configuration just before marshalling/unmarshalling. And so – if we require the instance to be configured on-the-fly using annotations, it is generally a good idea to use a separate XStream instance for each thread.

11. Conclusion

In this article, we covered the basics of using XStream to convert objects to XML. We also learned about customizations we can use to ensure the XML output meets our needs. Finally, we looked at thread-safety problems with annotations.

In the next article in this series, we will learn about converting XML back to Java objects.

The complete source code for this article can be downloaded from the linked GitHub repository.