Руководство пользователя XStream: преобразование объектов в XML

Руководство пользователя 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.