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

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

Вступление

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

Расширяемый язык разметки , широко известный как 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.

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

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

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

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

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

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

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

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

 <dependencies> 
 <dependency> 
 <groupId>junit</groupId> 
 <artifactId>junit</artifactId> 
 <version>3.8.1</version> 
 <scope>test</scope> 
 </dependency> 
 
 <!-- Jackson dependency for XML manipulation --> 
 <dependency> 
 <groupId>com.fasterxml.jackson.dataformat</groupId> 
 <artifactId>jackson-dataformat-xml</artifactId> 
 <version>2.9.0</version> 
 </dependency> 
 </dependencies> 
 
 <build> 
 <plugins> 
 <!-- 
 This plugin configuration will enable Maven to include the project dependencies 
 in the produced jar file. 
 It also enables us to run the jar file using `java -jar command` 
 --> 
 <plugin> 
 <groupId>org.apache.maven.plugins</groupId> 
 <artifactId>maven-shade-plugin</artifactId> 
 <version>3.2.0</version> 
 <executions> 
 <execution> 
 <phase>package</phase> 
 <goals> 
 <goal>shade</goal> 
 </goals> 
 <configuration> 
 <transformers> 
 <transformer 
 implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> 
 <mainClass>com.stackabuse.App</mainClass> 
 </transformer> 
 </transformers> 
 </configuration> 
 </execution> 
 </executions> 
 </plugin> 
 </plugins> 
 </build> 

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

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

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

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

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

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

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

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

 public class PhoneDetails { 
 private String name; 
 private String displaySize; 
 private String memory; 
 
 // getters and setters 
 } 

С нашим набором объектов давайте App.java наш 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 

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

 <PhoneDetails><name>OnePlus</name><displaySize>6.4</displaySize><memory>6/64 GB</memory></PhoneDetails> 

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

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

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

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

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

 <PhoneDetails> 
 <name>iPhone</name> 
 <displaySize>6.2</displaySize> 
 <memory>3/64 GB</memory> 
 </PhoneDetails> 

Давайте добавим функцию 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 

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

Аннотации Джексона

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

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

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

 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 должны будут напоминать строки в аннотации следующим образом:

 <PhoneDetails> 
 <phone_name>OnePlus</phone_name> 
 <display_size>6.4</display_size> 
 <internal_memory>6/64 GB</internal_memory> 
 </PhoneDetails> 

Еще одна примечательная аннотация - @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 теперь будет:

 <PhoneDetails> 
 <internal_memory>6/64 GB</internal_memory> 
 <display_size>6.4</display_size> 
 <phone_name>OnePlus</phone_name> 
 </PhoneDetails> 

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

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

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

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

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

 <PhoneDetails> 
 <internal_memory>3/64 GB</internal_memory> 
 <display_size>6.2</display_size> 
 <phone_name>iPhone X</phone_name> 
 <manufacturer> 
 <manufacturer_name>Apple</manufacturer_name> 
 <country>USA</country> 
 <other_phones> 
 <phone>iPhone 8</phone> 
 <phone>iPhone 7</phone> 
 <phone>iPhone 6</phone> 
 </other_phones> 
 </manufacturer> 
 </PhoneDetails> 

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

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

 // 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 
 @JacksonXmlElementWrapper(localName="other_phones") 
 private List<String> phone; 
 
 ... 

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

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

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

 // existing code remains 
 public class PhoneDetails { 
 // existing code remains 
 @JsonProperty("manufacturer") 
 private Manufacturer manufacturer; 
 
 // standard getters and setters for the new element 
 
 ... 

Наш PhoneDetails теперь может включать информацию о производителе телефона.

Затем мы обновляем наш метод serializeToXML()

 public static void serializeToXML() { 
 try { 
 XmlMapper xmlMapper = new XmlMapper(); 
 
 // create a list of other phones 
 List<String> 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 
 } 
 } 

Результат сериализации нового объекта PhoneDetails с информацией Manufacturer

 Serializing to XML... 
 
 <PhoneDetails><internal_memory>6/64 GB</internal_memory><display_size>6.4</display_size><phone_name>OnePlus</phone_name><manufacturer><manufacturer_name>OnePlus</manufacturer_name><country>China</country><other_phones><phones>OnePlus 6T</phones><phones>OnePlus 5T</phones><phones>OnePlus 5</phones></other_phones></manufacturer></PhoneDetails> 

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

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

 // 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 для справки.

comments powered by Disqus