Guida per l'utente XStream: conversione di oggetti in XML

1. Panoramica

In questo tutorial, impareremo come utilizzare la libreria XStream per serializzare oggetti Java in XML.

2. Caratteristiche

Ci sono alcuni vantaggi interessanti nell'usare XStream per serializzare e deserializzare XML:

  • Configurato correttamente, produce XML molto pulito
  • Fornisce opportunità significative per la personalizzazione dell'output XML
  • Supporto per grafici a oggetti , inclusi riferimenti circolari
  • Per la maggior parte dei casi d'uso, l'istanza XStream è thread-safe, una volta configurata (ci sono avvertenze quando si usano le annotazioni)
  • Vengono forniti messaggi chiari durante la gestione delle eccezioni per aiutare a diagnosticare i problemi
  • A partire dalla versione 1.4.7, sono disponibili funzionalità di sicurezza per non consentire la serializzazione di determinati tipi

3. Configurazione del progetto

Per utilizzare XStream nel nostro progetto aggiungeremo la seguente dipendenza Maven:

 com.thoughtworks.xstream xstream 1.4.9 

4. Utilizzo di base

La classe XStream è una facciata per l'API. Quando creiamo un'istanza di XStream , dobbiamo occuparci anche dei problemi di sicurezza dei thread:

XStream xstream = new XStream();

Una volta creata e configurata, un'istanza può essere condivisa su più thread per il marshalling / unmarshalling a meno che non si abiliti l'elaborazione delle annotazioni.

4.1. Autisti

Sono supportati diversi driver, come DomDriver , StaxDriver , XppDriver e altri. Questi driver hanno prestazioni e caratteristiche di utilizzo delle risorse diverse.

Il driver XPP3 viene utilizzato di default, ma ovviamente possiamo cambiare facilmente il driver:

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

4.2. Generazione di XML

Cominciamo definendo un semplice POJO per - Cliente :

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

Generiamo ora una rappresentazione XML dell'oggetto:

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

Utilizzando le impostazioni predefinite, viene prodotto il seguente output:

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

Da questo output, possiamo vedere chiaramente che il tag contenitore utilizza per impostazione predefinita il nome della classe completo di Customer .

Ci sono molte ragioni per cui potremmo decidere che il comportamento predefinito non soddisfa le nostre esigenze. Ad esempio, potremmo non sentirci a nostro agio esponendo la struttura del pacchetto della nostra applicazione. Inoltre, l'XML generato è notevolmente più lungo.

5. Alias

Un alias è un nome che desideriamo utilizzare per gli elementi piuttosto che utilizzare nomi predefiniti.

Ad esempio, possiamo sostituire com.baeldung.pojo.Customer con customer registrando un alias per la classe Customer . Possiamo anche aggiungere alias per le proprietà di una classe. Utilizzando gli alias, possiamo rendere il nostro output XML molto più leggibile e meno specifico per Java.

5.1. Alias ​​di classe

Gli alias possono essere registrati a livello di codice o utilizzando annotazioni.

Annotiamo ora la nostra classe Customer con @XStreamAlias :

@XStreamAlias("customer")

Ora dobbiamo configurare la nostra istanza per utilizzare questa annotazione:

xstream.processAnnotations(Customer.class);

In alternativa, se desideriamo configurare un alias in modo programmatico, possiamo utilizzare il codice seguente:

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

Sia che si utilizzi l'alias o la configurazione programmatica, l'output per un oggetto Customer sarà molto più pulito:

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

5.2. Alias ​​di campo

Possiamo anche aggiungere alias per i campi usando la stessa annotazione usata per le classi di alias. Ad esempio, se volessimo sostituire il campo firstName con fn nella rappresentazione XML, potremmo usare la seguente annotazione:

@XStreamAlias("fn") private String firstName;

In alternativa, possiamo raggiungere lo stesso obiettivo in modo programmatico:

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

Il metodo aliasField accetta tre argomenti: l'alias che si desidera utilizzare, la classe in cui è definita la proprietà e il nome della proprietà che si desidera alias.

Qualunque sia il metodo utilizzato, l'output è lo stesso:

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

5.3. Alias ​​predefiniti

Esistono diversi alias preregistrati per le classi: eccone alcuni:

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. Collezioni

Ora aggiungeremo un elenco di ContactDetails all'interno della classe Customer .

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.