Рубрики
Без рубрики

Сериализация и десериализация XML в Java с помощью Jackson

Автор оригинала: Robley Gori.

Вступление

Во все более взаимосвязанной экосистеме программных систем связь между ними становится еще более важной. В свою очередь, было разработано несколько технологий для упаковки данных, передаваемых или совместно используемых между этими многочисленными и различными системами.

Расширяемый язык разметки , известный в народе как XML , является одним из способов упаковки передаваемых данных. XML-это язык форматирования документов, который был разработан в 1990-х годах, поскольку HTML не позволяет определять новые текстовые элементы, т. е. он не является расширяемым. В дополнение к расширяемости данные в XML являются самоописываемыми, что делает их удобочитаемыми и простыми для понимания.

В этом посте мы рассмотрим манипуляции с XML в Java с использованием библиотеки Джексона .

Преимущества и недостатки XML

XML по-прежнему популярен и используется в некоторых системах, поскольку у него есть некоторые преимущества, но также появились новые технологии, чтобы устранить некоторые из его недостатков.

Некоторые из преимуществ XML включают:

  • XML не привязан к какой-либо одной платформе или языку программирования и может легко использоваться во многих различных системах. Это делает его подходящим для облегчения связи между системами с различными конфигурациями аппаратного и программного обеспечения.
  • Данные, содержащиеся в XML-документе, могут быть проверены с помощью определения типа документа (DTD) или схемы XML. Это набор объявлений разметки, которые определяют строительные блоки XML – документа.
  • Благодаря поддержке Unicode XML может содержать информацию, написанную на любом языке или формате, без потери какой-либо информации или содержимого в процессе.
  • Благодаря совместимости с HTML, с помощью HTML легко читать и отображать данные, содержащиеся в XML-документе.
  • Информация, хранящаяся в XML-документе, может быть изменена в любой момент времени, не влияя на представление данных с помощью других средств, таких как HTML.

Некоторые из недостатков XML, которые были устранены в новых технологиях, включают:

  • Синтаксис довольно избыточен и подробен по сравнению с другими форматами, такими как JSON, которые являются короткими и прямыми по существу.
  • Из-за своего синтаксиса и подробного характера XML-документы обычно имеют большой размер, что может привести к дополнительным затратам на хранение и транспортировку.
  • Он не поддерживает массивы.

Библиотеки XML

Манипулирование XML в Java может быть утомительным процессом, поэтому для облегчения процесса и ускорения разработки мы можем использовать различные библиотеки. Они включают в себя:

  • Eaxy , которая представляет собой небольшую и простую библиотеку для создания, обработки, анализа и поиска XML.
  • Архитектура Java для привязки XML (JAXB) – это платформа для сопоставления классов Java с представлениями XML посредством маршалинга объектов Java в XML и разбиения XML на объекты Java. Это часть платформы Java SE.
  • Jackson – это библиотека для обработки JSON в системах Java, которая теперь поддерживает XML версии 2.
  • DOM4J -это библиотека с эффективной памятью для анализа XML, XPath и XSLT (расширяемый язык таблиц стилей).
  • JDom , представляющая собой библиотеку синтаксического анализа XML с поддержкой XPath и XSLT.

Что такое Джексон?

Проект Джексона представляет собой набор инструментов обработки данных для языка Java и платформы JVM. Он поддерживает широкий спектр форматов данных, таких как CSV, свойства Java, XML и YAML, с помощью компонентов расширения, поддерживающих определенный язык.

Компонент XML Джексона предназначен для чтения и записи XML-данных путем эмуляции работы JAXB, хотя и не окончательно.

В этой статье мы будем использовать библиотеку Джексона для сериализации объектов Java в XML и десериализации их обратно в объекты Java.

Настройка проекта

Во – первых, давайте создадим новый проект Maven:

$ mvn archetype:generate -DgroupId=com.stackabuse -DartifactId=xmltutorial -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Сгенерировав наш проект, позвольте нам добавить зависимость Джексона в ваш pom.xml файл. Удалите раздел “Существующие зависимости” и замените его:


  
    junit
    junit
    3.8.1
    test
  

  
  
    com.fasterxml.jackson.dataformat
    jackson-dataformat-xml
    2.9.0
  



  
    
    
      org.apache.maven.plugins
      maven-shade-plugin
      3.2.0
      
        
          package
          
            shade
          
          
            
              
                com.stackabuse.App
              
            
          
        
      
    
  

Теперь мы можем протестировать созданный нами проект, выполнив следующие команды:

$ mvn package
$ java -jar target/java -jar target/xmltutorial-1.0.jar

Вывод должен быть Привет, Мир! печатается на нашем терминале, показывая, что наш проект готов к следующему этапу проекта.

Сериализация объектов Java в XML

Объекты Java имеют атрибуты и методы для управления этими атрибутами. В отношении XML – документа элементы в документе могут быть сопоставлены с атрибутами объекта Java.

В процессе сериализации атрибуты объекта преобразуются в XML-элементы и сохраняются в XML-документе.

Мы будем использовать класс Сведения о телефоне , который будет определять информацию о конкретной модели телефона, такую как ее название, размер дисплея и емкость внутренней памяти. В нашем классе это будут атрибуты, но в нашем XML-документе эти сведения будут содержаться в тегах или элементах.

Давайте начнем с определения Сведений о телефоне класса, который будет использоваться для создания ваших объектов:

public class PhoneDetails {
    private String name;
    private String displaySize;
    private String memory;

    // getters and setters
}

С помощью нашего набора объектов давайте изменим наш App.java и добавьте функцию для обработки сериализации в XML:

/**
* This function writes serializes the Java object into XML and writes it
* into an XML file.
*/
public static void serializeToXML() {
    try {
        XmlMapper xmlMapper = new XmlMapper();

        // serialize our Object into XML string
        String xmlString = xmlMapper.writeValueAsString(new PhoneDetails("OnePlus", "6.4", "6/64 GB"));

        // write to the console
        System.out.println(xmlString);

        // write XML string to file
        File xmlOutput = new File("serialized.xml");
        FileWriter fileWriter = new FileWriter(xmlOutput);
        fileWriter.write(xmlString);
        fileWriter.close();
    } catch (JsonProcessingException e) {
        // handle exception
    } catch (IOException e) {
        // handle exception
    }
}

public static void main(String[] args) {
    System.out.println("Serializing to XML...");
    serializeToXML();
}

Давайте упакуем и запустим наш проект еще раз:

$ mvn package
$ java -jar target/xmltutorial-1.0.jar

Вывод на терминале является:

OnePlus6.46/64 GB

В корневой папке вашего проекта serialized.xml создается файл, содержащий эту информацию. Мы успешно сериализовали наш объект Java в XML и записали его в XML-файл.

В нашей функции serializeToXML() мы создаем объект Xml-сопоставитель , который является дочерним классом класса ObjectMapper , используемого в сериализации JSON. Этот класс преобразует наш объект Java в выходные данные XML, которые мы теперь можем записать в файл.

Десериализация из XML

Джексон также позволяет нам считывать содержимое XML-файла и десериализовывать XML-строку обратно в объект Java. В нашем примере мы прочитаем XML-документ, содержащий подробную информацию о телефоне, и с помощью Джексона извлекем эти данные и используем их для создания объектов Java, содержащих ту же информацию.

Во-первых, давайте создадим XML-документ, соответствующий нашему классу для чтения. Создать to_deserialize.xml со следующим содержанием:


  iPhone
  6.2
  3/64 GB

Давайте добавим функцию deserializeFromXML() для десериализации XML-файла выше в объект Java:

public static void deserializeFromXML() {
    try {
        XmlMapper xmlMapper = new XmlMapper();

        // read file and put contents into the string
        String readContent = new String(Files.readAllBytes(Paths.get("to_deserialize.xml")));

        // deserialize from the XML into a Phone object
        PhoneDetails deserializedData = xmlMapper.readValue(readContent, PhoneDetails.class);

        // Print object details
        System.out.println("Deserialized data: ");
        System.out.println("\tName: " + deserializedData.getName());
        System.out.println("\tMemory: " + deserializedData.getMemory());
        System.out.println("\tDisplay Size: " + deserializedData.getDisplaySize());
    } catch (IOException e) {
        // handle the exception
    }
}

public static void main(String[] args) {
    System.out.println("Deserializing from XML...");
    deserializeFromXML();
}

Мы упаковываем и запускаем наш проект как обычно, и результат:

Deserializing from XML...

Deserialized data:
    Name: iPhone
    Memory: 3/64 GB
    Display Size: 6.2

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Наш XML-файл был успешно десериализован, и все данные были извлечены с помощью библиотеки Джексона.

Примечания Джексона

Аннотации используются для добавления метаданных в наш Java-код и не оказывают прямого влияния на выполнение кода, к которому они прикреплены. Они используются для предоставления инструкций компилятору во время компиляции и во время выполнения.

Джексон использует аннотации для различных функций, таких как определение того, сопоставляем ли мы XML или JSON, определение порядка атрибутов и полей в наших выходных данных или их имен.

Эти аннотации обычно применяются в наших Java POJOs (простых старых объектах Java). Например, мы можем аннотировать наши Телефонные данные класс следующим образом:

public class PhoneDetails {

    @JsonProperty("phone_name")
    private String name;

    @JsonProperty("display_size")
    private String displaySize;

    @JsonProperty("internal_memory")
    private String memory;

    // rest of the code remains as is
}

Аннотация @JsonProperty помогает определить имена полей в нашем XML-файле. С добавлением этой аннотации теги в наших выходных и входных файлах XML должны будут напоминать строки в аннотации следующим образом:


  OnePlus
  6.4
  6/64 GB

Другой примечательной аннотацией является @JacksonXmlText , которая указывает, что элемент должен отображаться в виде обычного текста без каких-либо тегов или другого элемента, содержащего его.

Аннотацию @JacksonXmlProperty можно использовать для управления деталями отображаемого атрибута или элемента. Такие сведения могут включать пространство имен элемента. Пространства имен-это способ присвоения элементов определенной группе.

Одним из основных применений пространств имен является предотвращение конфликтов при использовании похожих тегов в документе, они помогают изолировать теги по группам, чтобы устранить любую двусмысленность, которая может возникнуть при масштабировании XML-документов.

Порядок свойств также можно указать с помощью аннотации @JsonPropertyOrder . Например, чтобы изменить порядок элементов в выходных данных XML-документа, аннотация используется следующим образом:

@JsonPropertyOrder({ "internal_memory", "display_size", "phone_name" })
public class PhoneDetails {

    @JsonProperty("phone_name")
    private String name;

    @JsonProperty("display_size")
    private String displaySize;

    @JsonProperty("internal_memory")
    private String memory;

    ...

Вывод сериализации в XML теперь будет:


  6/64 GB
  6.4
  OnePlus

Если в объектах Java есть поля, которые мы не хотим сериализовать, мы можем использовать аннотацию @JsonIgnore , и поля будут опущены во время сериализации и десериализации.

Аннотации Джексона полезны для определения и управления процессом сериализации и десериализации в различных форматах, таких как XML, JSON и YAML. Некоторые аннотации работают для всех форматов, а некоторые привязаны к определенному типу файла.

Более подробные аннотации Джексона и их использование можно найти в этой официальной вики на Github.

Управление вложенными элементами и списками в XML

Изучив аннотации, давайте расширим наш XML-файл, добавив вложенные элементы и циклы, и изменим наш код для сериализации и десериализации следующей обновленной структуры:


  3/64 GB
  6.2
  iPhone X
  
    Apple
    USA
    
      iPhone 8
      iPhone 7
      iPhone 6
    
  

В этой новой структуре мы ввели вложенный элемент Производитель , который также включает список элементов. С нашим текущим кодом мы не можем извлечь или создать новый вложенный раздел.

Чтобы исправить это, требуется новый класс для обработки вложенного элемента, и с этой целью он является частью нашего нового Производителя класса:

// define the order of elements
@JsonPropertyOrder({ "manufacturer_name", "country", "other_phones" })
public class Manufacturer {
    @JsonProperty("manufacturer_name")
    private String name;

    @JsonProperty("country")
    private String country;

    // new annotation
    private List phone;

    ...

Это очень похоже на ваш Данные телефона класс, но теперь мы ввели новую аннотацию: @JacksonXmlElementWrapper . Цель этой аннотации-определить, использует ли коллекция элементов элемент-оболочку или нет, и может использоваться для указания локального имени и пространства имен элементов-оболочек.

В нашем примере мы используем аннотацию для определения элемента, содержащего список элементов, и тега, который будет использоваться для этого элемента. Это будет использоваться при сериализации и десериализации наших XML-файлов.

Это изменение в нашей структуре XML и введение этого класса требует, чтобы мы изменили ваши Данные телефона класс, чтобы отразить:

// existing code remains
public class PhoneDetails {
    // existing code remains
    @JsonProperty("manufacturer")
    private Manufacturer manufacturer;

    // standard getters and setters for the new element

    ...

Наш Сведения о телефоне объект теперь сможет включать информацию о производителе телефона.

Затем мы обновим наш метод serializeToXML() :

public static void serializeToXML() {
    try {
        XmlMapper xmlMapper = new XmlMapper();

        // create a list of other phones
        List otherPhones = Arrays.asList("OnePlus 6T", "OnePlus 5T", "OnePlus 5");

        // create the manufacturer object
        Manufacturer manufacturer = new Manufacturer("OnePlus", "China", otherPhones);

        // serialize our new Object into XML string
        String xmlString = xmlMapper
          .writeValueAsString(new PhoneDetails("OnePlus", "6.4", "6/64 GB", manufacturer));

        // write to the console
        System.out.println(xmlString);

        // write XML string to file
        File xmlOutput = new File("serialized.xml");
        FileWriter fileWriter = new FileWriter(xmlOutput);
        fileWriter.write(xmlString);
        fileWriter.close();
    } catch (JsonProcessingException e) {
        // handle the exception
    } catch (IOException e) {
        // handle the exception
    }
}

Результатом сериализации новых Телефонных данных объекта с Производителем информацией является:

Serializing to XML...

6/64 GB6.4OnePlusOnePlusChinaOnePlus 6TOnePlus 5TOnePlus 5

Это работает! С другой стороны, наша функция deserializeFromXML() не нуждается в серьезном обновлении, поскольку класс Сведения о телефоне при десериализации также будет содержать информацию о производителе.

Давайте добавим следующий код, чтобы распечатать данные производителя просто для уверенности:

// existing code remains

// Print object details
System.out.println("Deserialized data: ");
System.out.println("\tName: " + deserializedData.getName());
System.out.println("\tMemory: " + deserializedData.getMemory());
System.out.println("\tDisplay Size: " + deserializedData.getDisplaySize());
System.out.println("\tManufacturer Name: " + deserializedData.getManufacturer().getName());
System.out.println("\tManufacturer Country: " + deserializedData.getManufacturer().getCountry());
System.out.println("\tManufacturer Other Phones: " + deserializedData.getManufacturer().getPhone().toString());

// existing code remains

Вывод:

Deserializing from XML...

Deserialized data:
    Name: iPhone X
    Memory: 3/64 GB
    Display Size: 6.2
    Manufacturer Name: Apple
    Manufacturer Country: USA
    Manufacturer Other Phones: [iPhone 8, iPhone 7, iPhone 6]

Процесс десериализации проходит гладко, и новые сведения о производителе были извлечены из нашего обновленного XML-файла.

Вывод

В этом посте мы узнали о XML и о том, как сериализовать данные в XML-документы, а также десериализовать их для извлечения данных из XML-документов.

Мы также узнали об аннотациях и о том, как Джексон использует аннотации в процессе сериализации и десериализации.

XML по – прежнему широко используется в различных системах, с которыми мы можем время от времени взаимодействовать, поэтому для взаимодействия с ними нам время от времени потребуется сериализовать и десериализовать XML-документы. Мы также можем использовать XML-API в наших проектах Java, предоставляя конечные точки REST, и использовать Jackson для преобразования входных данных XML в выходные данные JSON.

Исходный код этого поста доступен на Github для справки.