1. Обзор
В этом руководстве мы узнаем, как использовать библиотеку XStream для сериализации объектов Java в XML.
2. Особенности
Есть несколько интересных преимуществ использования XStream для сериализации и десериализации XML:
- При правильной настройке он создает очень чистый XML.
- Предоставляет значительные возможности для настройки вывода XML
- Поддержка графов объектов , включая циклические ссылки
- В большинстве случаев экземпляр XStream является потокобезопасным после настройки (есть предостережения при использовании аннотаций).
- Во время обработки исключений предоставляются четкие сообщения, помогающие диагностировать проблемы.
- Начиная с версии 1.4.7, у нас есть функции безопасности, позволяющие запретить сериализацию определенных типов.
3. Настройка проекта
Чтобы использовать XStream в нашем проекте, мы добавим следующую зависимость Maven:
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.18</version>
</dependency>
4. Основное использование
Класс XStream
— это фасад для API. При создании экземпляра XStream
нам также необходимо позаботиться о проблемах безопасности потоков:
XStream xstream = new XStream();
После создания и настройки экземпляра его можно использовать в нескольких потоках для маршалинга/демаршаллинга, если только вы не включите обработку аннотаций.
4.1. Драйверы
Поддерживается несколько драйверов, таких как DomDriver
, StaxDriver
, XppDriver
и другие. Эти драйверы имеют разные характеристики производительности и использования ресурсов.
Драйвер XPP3 используется по умолчанию, но, конечно, мы можем легко изменить драйвер:
XStream xstream = new XStream(new StaxDriver());
4.2. Генерация XML
Начнем с определения простого POJO для — Customer
:
public class Customer {
private String firstName;
private String lastName;
private Date dob;
// standard constructor, setters, and getters
}
Давайте теперь сгенерируем XML-представление объекта:
Customer customer = new Customer("John", "Doe", new Date());
String dataXml = xstream.toXML(customer);
Используя настройки по умолчанию, создается следующий вывод:
<com.foreach.pojo.Customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</com.foreach.pojo.Customer>
Из этого вывода ясно видно, что содержащий тег по умолчанию использует полное имя класса Customer
.
Есть много причин, по которым мы можем решить, что поведение по умолчанию не соответствует нашим потребностям. Например, нам может быть неудобно раскрывать структуру пакета нашего приложения. Кроме того, сгенерированный XML значительно длиннее.
5. Псевдонимы
Псевдоним — это имя , которое мы хотим использовать для элементов, а не использовать имена по умолчанию.
Например, мы можем заменить com.foreach.pojo.Customer
на customer
, зарегистрировав псевдоним для класса Customer .
Мы также можем добавить псевдонимы для свойств класса. Используя псевдонимы, мы можем сделать вывод XML более читабельным и менее специфичным для Java.
5.1. Псевдонимы классов
Псевдонимы могут быть зарегистрированы либо программно, либо с помощью аннотаций.
Давайте теперь аннотируем наш класс Customer с помощью
@XStreamAlias
:
@XStreamAlias("customer")
Теперь нам нужно настроить наш экземпляр для использования этой аннотации:
xstream.processAnnotations(Customer.class);
В качестве альтернативы, если мы хотим настроить псевдоним программно, мы можем использовать следующий код:
xstream.alias("customer", Customer.class);
Независимо от того, используете ли вы псевдоним или программную конфигурацию, вывод для объекта Customer
будет намного чище:
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>
5.2. Псевдонимы полей
Мы также можем добавить псевдонимы для полей, используя ту же аннотацию, которая используется для псевдонимов классов. Например, если мы хотим, чтобы поле firstName
было заменено на fn
в представлении XML, мы могли бы использовать следующую аннотацию:
@XStreamAlias("fn")
private String firstName;
Кроме того, мы можем достичь той же цели программно:
xstream.aliasField("fn", Customer.class, "firstName");
Метод aliasField
принимает три аргумента: псевдоним, который мы хотим использовать, класс, в котором определено свойство, и имя свойства, которое мы хотим использовать как псевдоним.
Какой бы метод ни использовался, вывод будет одинаковым:
<customer>
<fn>John</fn>
<lastName>Doe</lastName>
<dob>1986-02-14 03:46:16.381 UTC</dob>
</customer>
5.3. Псевдонимы по умолчанию
Для классов предварительно зарегистрированы несколько псевдонимов — вот некоторые из них:
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. Коллекции
Теперь мы добавим список ContactDetails
в класс Customer .
private List<ContactDetails> contactDetailsList;
С настройками по умолчанию для обработки коллекций это результат:
<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>
Предположим, нам нужно опустить родительские теги contactDetailsList
,
и мы просто хотим, чтобы каждый элемент ContactDetails был дочерним элементом элемента
customer
. Давайте снова изменим наш пример:
xstream.addImplicitCollection(Customer.class, "contactDetailsList");
Now, when the XML is generated, the root tags are omitted, resulting in the XML below:
<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>
То же самое можно сделать и с помощью аннотаций:
@XStreamImplicit
private List<ContactDetails> contactDetailsList;
7. Преобразователи
XStream использует карту экземпляров Converter
, каждый со своей собственной стратегией преобразования. Они преобразуют предоставленные данные в определенный формат XML и обратно.
Помимо использования преобразователей по умолчанию, мы можем изменить значения по умолчанию или зарегистрировать пользовательские преобразователи.
7.1. Изменение существующего преобразователя
Предположим, нас не устраивает способ создания тегов dob
` с использованием настроек по умолчанию. Мы можем изменить пользовательский преобразователь для
даты , предоставленный XStream (
DateConverter` ):
xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));
Приведенное выше выведет вывод в формате « дд-мм-гггг
»:
<customer>
<firstName>John</firstName>
<lastName>Doe</lastName>
<dob>14-02-1986</dob>
</customer>
7.2. Пользовательские конвертеры
Мы также можем создать собственный преобразователь для получения того же результата, что и в предыдущем разделе:
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
}
Наконец, мы регистрируем наш класс MyDateConverter
, как показано ниже:
xstream.registerConverter(new MyDateConverter());
Мы также можем создавать преобразователи, реализующие интерфейс 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
}
Наконец, мы регистрируем MySingleValueConverter
:
xstream.registerConverter(new MySingleValueConverter());
При использовании MySingleValueConverter
вывод XML для клиента
выглядит следующим образом:
<customer>John,Doe,14-02-1986</customer>
7.3. Приоритет конвертера
При регистрации объектов Converter
также можно задать уровень их приоритета.
Из javadocs XStream :
Преобразователи могут быть зарегистрированы с явным приоритетом. По умолчанию они зарегистрированы в XStream.PRIORITY_NORMAL. Преобразователи того же приоритета будут использоваться в обратной последовательности, в которой они были зарегистрированы. Преобразователь по умолчанию, то есть преобразователь, который будет использоваться, если никакой другой зарегистрированный преобразователь не подходит, может быть зарегистрирован с приоритетом XStream.PRIORITY_VERY_LOW. XStream по умолчанию использует ReflectionConverter в качестве резервного преобразователя.
API предоставляет несколько именованных значений приоритета: **
**
private static final int PRIORITY_NORMAL = 0;
private static final int PRIORITY_LOW = -10;
private static final int PRIORITY_VERY_LOW = -20;
8. Пропуск полей
Мы можем опустить поля из нашего сгенерированного XML, используя либо аннотации, либо программную конфигурацию. Чтобы опустить поле с помощью аннотации, мы просто применяем аннотацию @XStreamOmitField
к рассматриваемому полю:
@XStreamOmitField
private String firstName;
Чтобы программно опустить поле, мы используем следующий метод:
xstream.omitField(Customer.class, "firstName");
Какой бы метод мы ни выбрали, вывод будет одинаковым:
<customer>
<lastName>Doe</lastName>
<dob>14-02-1986</dob>
</customer>
9. Поля атрибутов
Иногда нам может понадобиться сериализовать поле как атрибут элемента, а не как сам элемент. Предположим, мы добавляем поле contactType
:
private String contactType;
Если мы хотим установить contactType
в качестве XML-атрибута, мы можем использовать аннотацию @XStreamAsAttribute
:
@XStreamAsAttribute
private String contactType;
Кроме того, мы можем достичь той же цели программно:
xstream.useAttributeFor(ContactDetails.class, "contactType");
Результат любого из вышеперечисленных методов одинаков:
<ContactDetails contactType="Office">
<mobile>6673543265</mobile>
<landline>0124-2460311</landline>
</ContactDetails>
10. Параллелизм
Модель обработки XStream представляет некоторые проблемы. После того, как экземпляр сконфигурирован, он является потокобезопасным.
Важно отметить, что обработка аннотаций изменяет конфигурацию непосредственно перед маршалингом/демаршаллингом. Итак, если нам требуется, чтобы экземпляр настраивался «на лету» с помощью аннотаций, обычно рекомендуется использовать отдельный экземпляр XStream
для каждого потока.
11. Заключение
В этой статье мы рассмотрели основы использования XStream для преобразования объектов в XML. Мы также узнали о настройках, которые мы можем использовать, чтобы обеспечить соответствие вывода XML нашим потребностям. Наконец, мы рассмотрели проблемы безопасности потоков с помощью аннотаций.
В следующей статье этой серии мы узнаем о преобразовании XML обратно в объекты Java.
Полный исходный код для этой статьи можно скачать из связанного репозитория GitHub .