XStream User Guide: Converting Objects to XML – XStream用户指南 将对象转换为XML

最后修改: 2016年 3月 31日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll learn how to use the XStream library to serialize Java objects to XML.

在本教程中,我们将学习如何使用XStream库来将Java对象序列化为XML。

2. Features

2.特点

There are quite a few interesting benefits to using XStream to serialize and deserialize XML:

使用XStream对XML进行序列化和反序列化有不少有趣的好处。

  • Configured properly, it produces very clean XML
  • Provides significant opportunities for customization of the XML output
  • Support for object graphs, including circular references
  • For most use cases, the XStream instance is thread-safe, once configured (there are caveats when using annotations)
  • Clear messages are provided during exception handling to help diagnose issues
  • Starting with version 1.4.7, we have security features available to disallow serialization of certain types

3. Project Setup

3.项目设置

In order to use XStream in our project we will add the following Maven dependency:

为了在我们的项目中使用XStream,我们将添加以下Maven依赖项。

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.18</version>
</dependency>

4. Basic Usage

4.基本用法

The XStream class is a facade for the API. When creating an instance of XStream, we need to take care of thread safety issues as well:

XStream类是该API的一个门面。在创建XStream的实例时,我们也需要照顾到线程安全问题。

XStream xstream = new XStream();

Once an instance is created and configured, it may be shared across multiple threads for marshalling/unmarshalling unless you enable annotation processing.

一旦一个实例被创建和配置,它可能会在多个线程之间共享,以进行marshalling/unmarshalling,除非你启用注释处理。

4.1. Drivers

4.1.驱动程序

Several drivers are supported, such as DomDriver, StaxDriver, XppDriver, and more. These drivers have different performance and resource usage characteristics.

支持几个驱动程序,如DomDriver,StaxDriver,XppDriver,等等。这些驱动程序有不同的性能和资源使用特点。

The XPP3 driver is used by default, but of course we can easily change the driver:

默认使用XPP3驱动,当然我们可以很容易地改变驱动。

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

4.2. Generating XML

4.2.生成XML

Let’s start by defining a simple POJO for – Customer:

让我们先为-Customer定义一个简单的POJO。

public class Customer {

    private String firstName;
    private String lastName;
    private Date dob;

    // standard constructor, setters, and getters
}

Let’s now generate an XML representation of the object:

现在让我们为该对象生成一个XML表示。

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

Using the default settings, the following output is produced:

使用默认设置,会产生以下输出。

<com.baeldung.pojo.Customer>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <dob>1986-02-14 03:46:16.381 UTC</dob>
</com.baeldung.pojo.Customer>

From this output, we can clearly see that the containing tag uses the fully-qualified class name of Customer by default.

从这个输出中,我们可以清楚地看到,包含标签默认使用了Customer的全限定类名。

There are many reasons we might decide that the default behavior doesn’t suit our needs. For example, we might not be comfortable exposing the package structure of our application. Also, the XML generated is significantly longer.

有很多原因,我们可能决定默认行为不适合我们的需要。例如,我们可能不愿意暴露我们应用程序的包结构。另外,生成的XML明显较长。

5. Aliases

5.别名

An alias is a name we wish to use for elements rather than using default names.

一个alias是我们希望用于元素的名称,而不是使用默认名称。

For example, we can replace com.baeldung.pojo.Customer with customer by registering an alias for the Customer class. We can also add aliases for properties of a class. By using aliases, we can make our XML output much more readable and less Java-specific.

例如,我们可以通过为Customer类注册一个别名,将com.baeldung.pojo.Customer替换为customer。我们也可以为一个类的属性添加别名。通过使用别名,我们可以使我们的XML输出更加可读,并减少Java的特殊性。

5.1. Class Aliases

5.1.类的别名

Aliases can be registered either programmatically or using annotations.

别名可以通过编程或使用注解来注册。

Let’s now annotate our Customer class with @XStreamAlias:

现在让我们用@XStreamAlias来注解我们的Customer类。

@XStreamAlias("customer")

Now we need to configure our instance to use this annotation:

现在我们需要配置我们的实例来使用这个注解。

xstream.processAnnotations(Customer.class);

Alternatively, if we wish to configure an alias programmatically, we can use the code below:

另外,如果我们希望以编程方式配置别名,我们可以使用下面的代码。

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

Whether using the alias or programmatic configuration, the output for a Customer object will be much cleaner:

无论是使用别名还是程序化配置,Customer对象的输出都会更干净。

<customer>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>

5.2. Field Aliases

5.2.字段别名

We can also add aliases for fields using the same annotation used for aliasing classes. For example, if we wanted the field firstName to be replaced with fn in the XML representation, we could use the following annotation:

我们也可以使用用于别名类的相同注解为字段添加别名。例如,如果我们想让字段firstName在XML表示中被替换成fn,我们可以使用以下注解。

@XStreamAlias("fn")
private String firstName;

Alternatively, we can accomplish the same goal programmatically:

另外,我们也可以通过编程来实现同样的目标。

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

The aliasField method accepts three arguments: the alias we wish to use, the class in which the property is defined, and the property name we wish to alias.

aliasField方法接受三个参数:我们希望使用的别名,定义属性的类,以及我们希望别名的属性名称。

Whichever method is used the output is the same:

无论使用哪种方法,输出都是一样的。

<customer>
    <fn>John</fn>
    <lastName>Doe</lastName>
    <dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>

5.3. Default Aliases

5.3.默认别名

There are several aliases pre-registered for classes – here’s a few of these:

有几个别名预先登记了课程–这里是其中的几个。

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

6.收藏

Now we will add a list of ContactDetails inside the Customer class.

现在我们将在Customer类内添加一个ContactDetails列表。

private List<ContactDetails> contactDetailsList;

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

在收集处理的默认设置下,这就是输出。

<customer>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <dob>1986-02-14 04:14:05.874 UTC</dob>
    <contactDetailsList>
        <ContactDetails>
            <mobile>6673543265</mobile>
            <landline>0124-2460311</landline>
        </ContactDetails>
        <ContactDetails>
            <mobile>4676543565</mobile>
            <landline>0120-223312</landline>
        </ContactDetails>
    </contactDetailsList>
</customer>

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:

假设我们需要省略contactDetailsList parent tags,,我们只想让每个ContactDetails元素成为customer元素的孩子。让我们再次修改我们的例子。

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

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

现在,当XML被生成时,根标签被省略了,结果是下面的XML:

<customer>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <dob>1986-02-14 04:14:20.541 UTC</dob>
    <ContactDetails>
        <mobile>6673543265</mobile>
        <landline>0124-2460311</landline>
    </ContactDetails>
    <ContactDetails>
        <mobile>4676543565</mobile>
        <landline>0120-223312</landline>
    </ContactDetails>
</customer>

The same can also be achieved using annotations:

同样也可以使用注释来实现。

@XStreamImplicit
private List<ContactDetails> contactDetailsList;

7. Converters

7.转换器[/strong

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.

XStream使用一个Converter实例的映射,每个实例都有自己的转换策略。这些实例将提供的数据转换为XML中的特定格式,然后再转换回来。

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

除了使用默认的转换器,我们还可以修改默认的转换器或注册自定义的转换器。

7.1. Modifying an Existing Converter

7.1.修改一个现有的转换器

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

假设我们对使用默认设置生成的dobtags的方式不满意。我们可以修改XStream提供的Date的自定义转换器(DateConverter)。

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

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

以上将产生”dd-MM-yyy“格式的输出。

<customer>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <dob>14-02-1986</dob>
</customer>

7.2. Custom Converters

7.2.自定义转换器

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:

最后,我们注册我们的MyDateConverter类,如下所示。

xstream.registerConverter(new MyDateConverter());

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

我们还可以创建实现SingleValueConverter接口的转换器,该转换器旨在将一个对象转换为一个字符串。

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:

最后,我们注册MySingleValueConverter

xstream.registerConverter(new MySingleValueConverter());

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

使用MySingleValueConverter,一个Customer的XML输出如下。

<customer>John,Doe,14-02-1986</customer>

7.3. Converter Priority

7.3.转换器的优先权

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

在注册Converter对象时,也可以设置其优先级。

From the XStream javadocs:

来自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.

转换器可以用一个明确的优先级来注册。默认情况下,它们被注册为XStream.PRIORITY_NORMAL。相同优先级的转换器将按照它们被注册的相反顺序使用。默认转换器,即如果没有其他注册的转换器是合适的,将被使用的转换器,可以用优先级XStream.PRIORITY_VERY_LOW注册。XStream默认使用ReflectionConverter作为后备转换器。

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:

我们可以使用注解或程序化配置从我们生成的XML中省略字段。为了使用注解省略一个字段,我们只需将@XStreamOmitField注解应用于相关字段。

@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:

无论我们选择哪种方法,产出都是一样的。

<customer> 
    <lastName>Doe</lastName> 
    <dob>14-02-1986</dob> 
</customer>

9. Attribute Fields

9.属性字段

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:

有时我们可能希望将一个字段序列化为一个元素的属性,而不是元素本身。假设我们添加一个contactType字段。

private String contactType;

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

如果我们想把contactType设置为一个XML属性,我们可以使用@XStreamAsAttribute注释。

@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:

上述两种方法的输出结果都是一样的。

<ContactDetails contactType="Office">
    <mobile>6673543265</mobile>
    <landline>0124-2460311</landline>
</ContactDetails>

10. Concurrency

10.并发性

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

XStream的处理模式带来了一些挑战。一旦实例被配置好,它就是线程安全的。

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.

需要注意的是,对注释的处理会在打包/解包之前修改配置。因此–如果我们需要使用注释对实例进行即时配置,一般来说,为每个线程使用单独的XStream实例是个好主意。

11. Conclusion

11.结论

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.

在这篇文章中,我们介绍了使用XStream将对象转换为XML的基本知识。我们还了解了我们可以用来确保XML输出满足我们需求的定制。最后,我们研究了注解的线程安全问题。

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

在本系列的下一篇文章中,我们将学习如何将XML转换回Java对象。

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

本文的完整源代码可以从链接的GitHub 仓库下载。