Вступление
Во все более взаимосвязанной экосистеме программных систем связь между ними становится еще более важной. В свою очередь, было разработано несколько технологий для упаковки данных, передаваемых или совместно используемых между этими многочисленными и различными системами.
Расширяемый язык разметки , известный в народе как 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
Вывод на терминале является:
OnePlus 6.4 6/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 Listphone; ...
Это очень похоже на ваш Данные телефона
класс, но теперь мы ввели новую аннотацию: @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 ListotherPhones = 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 GB 6.4 OnePlus OnePlus China OnePlus 6T OnePlus 5T OnePlus 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 для справки.