Guía del usuario de XStream: conversión de objetos a XML

1. Información general

En este tutorial, aprenderemos cómo usar la biblioteca XStream para serializar objetos Java a XML.

2. Características

Existen bastantes beneficios interesantes al usar XStream para serializar y deserializar XML:

  • Configurado correctamente, produce XML muy limpio
  • Proporciona oportunidades importantes para la personalización de la salida XML.
  • Soporte para gráficos de objetos , incluidas referencias circulares
  • Para la mayoría de los casos de uso, la instancia XStream es segura para subprocesos, una vez configurada (hay advertencias al usar anotaciones)
  • Se proporcionan mensajes claros durante el manejo de excepciones para ayudar a diagnosticar problemas
  • A partir de la versión 1.4.7, tenemos funciones de seguridad disponibles para no permitir la serialización de ciertos tipos.

3. Configuración del proyecto

Para usar XStream en nuestro proyecto, agregaremos la siguiente dependencia de Maven:

 com.thoughtworks.xstream xstream 1.4.9 

4. Uso básico

La clase XStream es una fachada para la API. Al crear una instancia de XStream , también debemos ocuparnos de los problemas de seguridad de los subprocesos:

XStream xstream = new XStream();

Una vez que se crea y se configura una instancia, se puede compartir en varios subprocesos para ordenar / desagrupar a menos que habilite el procesamiento de anotaciones.

4.1. Conductores

Se admiten varios controladores, como DomDriver , StaxDriver , XppDriver y más. Estos controladores tienen diferentes características de uso de recursos y rendimiento.

El controlador XPP3 se usa por defecto, pero, por supuesto, podemos cambiarlo fácilmente:

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

4.2. Generando XML

Comencemos por definir un POJO simple para - Cliente :

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

Generemos ahora una representación XML del objeto:

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

Usando la configuración predeterminada, se produce la siguiente salida:

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

De esta salida, podemos ver claramente que la etiqueta contenedora usa el nombre de clase completamente calificado de Cliente de forma predeterminada .

Hay muchas razones por las que podríamos decidir que el comportamiento predeterminado no se adapta a nuestras necesidades. Por ejemplo, es posible que no nos sintamos cómodos al exponer la estructura del paquete de nuestra aplicación. Además, el XML generado es significativamente más largo.

5. Alias

Un alias es un nombre que deseamos usar para elementos en lugar de usar nombres predeterminados.

Por ejemplo, podemos reemplazar com.baeldung.pojo.Customer con customer registrando un alias para la clase Customer . También podemos agregar alias para las propiedades de una clase. Al usar alias, podemos hacer que nuestra salida XML sea mucho más legible y menos específica de Java.

5.1. Alias ​​de clase

Los alias se pueden registrar mediante programación o mediante anotaciones.

Anotemos ahora nuestra clase de Cliente con @XStreamAlias :

@XStreamAlias("customer")

Ahora necesitamos configurar nuestra instancia para usar esta anotación:

xstream.processAnnotations(Customer.class);

Alternativamente, si deseamos configurar un alias mediante programación, podemos usar el siguiente código:

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

Ya sea usando el alias o la configuración programática, la salida para un objeto Cliente será mucho más limpia:

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

5.2. Alias ​​de campo

También podemos agregar alias para los campos usando la misma anotación que se usa para las clases de alias. Por ejemplo, si quisiéramos que el campo firstName se reemplazara con fn en la representación XML, podríamos usar la siguiente anotación:

@XStreamAlias("fn") private String firstName;

Alternativamente, podemos lograr el mismo objetivo mediante programación:

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

El método aliasField acepta tres argumentos: el alias que deseamos usar, la clase en la que se define la propiedad y el nombre de la propiedad que deseamos alias.

Cualquiera que sea el método que se utilice, la salida es la misma:

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

5.3. Alias ​​predeterminados

Hay varios alias prerregistrados para las clases; aquí hay algunos de estos:

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

Ahora agregaremos una lista de ContactDetails dentro de la clase 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.