Руководство пользователя XStream: преобразование объектов в XML
1. обзор
В этом руководстве мы узнаем, как использовать библиотекуXStream для сериализации объектов Java в XML.
2. Характеристики
Использование XStream для сериализации и десериализации XML имеет немало интересных преимуществ:
-
При правильной настройке выдает оченьclean XML
-
Предоставляет значительные возможности дляcustomization вывода XML
-
Поддержкаobject graphs, включая циклические ссылки
-
Для большинства случаев использования экземпляр XStream -thread-safe, once configured (при использовании аннотаций есть предостережения)
-
В течениеexception handling отображаются четкие сообщения, помогающие диагностировать проблемы
-
Начиная с версии 1.4.7, у нас естьsecurity features для запрета сериализации определенных типов.
3. Настройка проекта
Чтобы использовать XStream в нашем проекте, мы добавим следующую зависимость Maven:
com.thoughtworks.xstream
xstream
1.4.9
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);
Используя настройки по умолчанию, получается следующий вывод:
John
Doe
1986-02-14 03:46:16.381 UTC
Из этого вывода мы можем ясно видеть, что содержащий тег использует полное имя классаCustomer по умолчанию.
Есть много причин, по которым мы можем решить, что поведение по умолчанию не соответствует нашим потребностям. Например, нам может быть неудобно показывать структуру пакета нашего приложения. Кроме того, сгенерированный XML значительно длиннее.
5. Псевдонимы
alias - это имя, которое мы хотим использовать для элементов, а не использовать имена по умолчанию.
Например, мы можем заменитьcom.example.pojo.Customer наcustomer, зарегистрировав псевдоним для классаCustomer. Мы также можем добавить псевдонимы для свойств класса. Используя псевдонимы, мы можем сделать наш вывод XML более читабельным и менее специфичным для Java.
5.1. Псевдонимы классов
Псевдонимы могут быть зарегистрированы либо программно, либо с использованием аннотаций.
Давайте теперь аннотируем наш классCustomer с помощью@XStreamAlias:
@XStreamAlias("customer")
Теперь нам нужно настроить наш экземпляр для использования этой аннотации:
xstream.processAnnotations(Customer.class);
В качестве альтернативы, если мы хотим настроить псевдоним программно, мы можем использовать код ниже:
xstream.alias("customer", Customer.class);
Независимо от того, используется ли псевдоним или программная конфигурация, вывод для объектаCustomer будет намного чище:
John
Doe
1986-02-14 03:46:16.381 UTC
5.2. Псевдонимы полей
Мы также можем добавить псевдонимы для полей, используя ту же аннотацию, что и для классов псевдонимов. Например, если мы хотим, чтобы полеfirstName было заменено наfn в XML-представлении, мы могли бы использовать следующую аннотацию:
@XStreamAlias("fn")
private String firstName;
В качестве альтернативы, мы можем достичь той же цели программно:
xstream.aliasField("fn", Customer.class, "firstName");
МетодaliasField принимает три аргумента: псевдоним, который мы хотим использовать, класс, в котором определено свойство, и имя свойства, которому мы хотим присвоить псевдоним.
Какой бы метод ни использовался, результат будет одинаковым:
John
Doe
1986-02-14 03:46:16.381 UTC
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 contactDetailsList;
С настройками по умолчанию для обработки коллекции, это вывод:
John
Doe
1986-02-14 04:14:05.874 UTC
6673543265
0124-2460311
4676543565
0120-223312
Предположим, нам нужно опустить родительские тегиcontactDetailsList,, и мы просто хотим, чтобы каждый элементContactDetails был дочерним по отношению к элементуcustomer. Давайте снова изменим наш пример:
xstream.addImplicitCollection(Customer.class, "contactDetailsList");
Теперь при генерации XML корневые теги опускаются, в результате получается XML-код ниже:
John
Doe
1986-02-14 04:14:20.541 UTC
6673543265
0124-2460311
4676543565
0120-223312
То же самое может быть достигнуто с помощью аннотаций:
@XStreamImplicit
private List contactDetailsList;
7. Преобразователи
XStream использует карту экземпляровConverter, каждый со своей собственной стратегией преобразования. Они преобразуют предоставленные данные в определенный формат в XML и обратно.
Помимо использования конвертеров по умолчанию, мы можем изменить настройки по умолчанию или зарегистрировать пользовательские конвертеры.
7.1. Изменение существующего конвертера
Предположим, нас не устраивает способ создания теговdob_ using the default settings. We can modify the custom converter for _Date, предоставленных XStream (DateConverter):
xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));
Приведенное выше приведет к выводу в формате «dd-MM-yyyy»:
John
Doe
14-02-1986
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
7.3. Приоритет конвертера
При регистрации объектовConverter также можно установить их уровень приоритета.
Преобразователи могут быть зарегистрированы с явным приоритетом. По умолчанию они зарегистрированы в 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");
Какой бы метод мы ни выбрали, результат будет одинаковым:
Doe
14-02-1986
9. Поля атрибутов
Иногда мы можем захотеть сериализовать поле как атрибут элемента, а не как сам элемент. Допустим, мы добавляем полеcontactType:
private String contactType;
Если мы хотим установитьcontactType как атрибут XML, мы можем использовать аннотацию@XStreamAsAttribute:
@XStreamAsAttribute
private String contactType;
В качестве альтернативы, мы можем достичь той же цели программно:
xstream.useAttributeFor(ContactDetails.class, "contactType");
Вывод любого из вышеуказанных методов одинаков:
6673543265
0124-2460311
10. совпадение
Модель обработки XStream представляет некоторые проблемы. Как только экземпляр настроен, он является потокобезопасным.
Важно отметить, что обработка аннотаций изменяет конфигурацию непосредственно перед маршалингом / демаршаллингом. Итак - если нам требуется, чтобы экземпляр настраивался на лету с использованием аннотаций, обычно рекомендуется использовать отдельный экземплярXStream для каждого потока.
11. Заключение
В этой статье мы рассмотрели основы использования XStream для преобразования объектов в XML. Мы также узнали о настройках, которые мы можем использовать для обеспечения того, чтобы вывод XML соответствовал нашим потребностям. Наконец, мы рассмотрели проблемы безопасности потоков с аннотациями.
В следующей статье этой серии мы узнаем о преобразовании XML обратно в объекты Java.
Полный исходный код этой статьи можно загрузить по ссылкеGitHub repository.