Serializar y deserializar XML en Java con Jackson

    Introducci贸n

    En un ecosistema cada vez m谩s conectado de sistemas de software, la comunicaci贸n entre ellos se ha vuelto a煤n m谩s primordial. A su vez, se han desarrollado varias tecnolog铆as para empaquetar los datos que se transfieren o comparten entre estos muchos y diferentes sistemas.

    los Lenguaje de marcado extensible, popularmente conocido como XML, es una de las formas de empaquetar los datos que se van a transferir. XML es un lenguaje de formato de documentos que se desarroll贸 en la d茅cada de 1990 ya que HTML no permite la definici贸n de nuevos elementos de texto, es decir, no es extensible. Adem谩s de ser extensible, los datos en XML son autodescriptivos, lo que los hace legibles y f谩ciles de comprender.

    En esta publicaci贸n, exploraremos la manipulaci贸n XML en Java usando el Biblioteca Jackson.

    Ventajas y desventajas de XML

    XML sigue siendo popular y se utiliza en algunos sistemas, ya que tiene algunas ventajas, pero tambi茅n han surgido tecnolog铆as m谩s nuevas para cubrir algunas de sus deficiencias.

    Algunas de las ventajas de XML incluyen:

    • XML no est谩 vinculado a una 煤nica plataforma o lenguaje de programaci贸n y se puede utilizar f谩cilmente en muchos sistemas diferentes. Esto lo hace adecuado para facilitar la comunicaci贸n entre sistemas con diferentes configuraciones de hardware y software.
    • Los datos contenidos en un documento XML se pueden validar mediante una definici贸n de tipo de documento (DTD) o un esquema XML. Este es un conjunto de declaraciones de marcado que definen los componentes b谩sicos de un documento XML.
    • A trav茅s de su soporte para Unicode, XML puede contener informaci贸n escrita en cualquier idioma o formato sin perder ninguna informaci贸n o contenido en el proceso.
    • Gracias a su compatibilidad con HTML, es f谩cil leer y mostrar los datos contenidos en un documento XML utilizando HTML.
    • La informaci贸n almacenada en un documento XML se puede modificar en cualquier momento sin afectar la presentaci贸n de los datos a trav茅s de otros medios como HTML.

    Algunas de las deficiencias de XML que se han resuelto en nuevas tecnolog铆as incluyen:

    • La sintaxis es bastante redundante y detallada en comparaci贸n con otros formatos, como JSON, que es breve y directo al grano.
    • Debido a su sintaxis y naturaleza detallada, los documentos XML suelen ser grandes, lo que puede generar costos adicionales de almacenamiento y transporte.
    • No tiene soporte para matrices.

    Bibliotecas XML

    Manipular XML en Java puede ser un proceso tedioso, por lo que para facilitar el proceso y acelerar el desarrollo, existen varias bibliotecas que podemos usar. Incluyen:

    • Eaxy que es una biblioteca peque帽a y simple para construir, manipular, analizar y buscar XML.
    • Arquitectura Java para enlaces XML (JAXB) es un marco para mapear clases de Java a representaciones XML mediante la ordenaci贸n de objetos Java en XML y la descomposici贸n de XML en objetos Java. Es parte de la plataforma Java SE.
    • Jackson es una biblioteca para manejar JSON en sistemas Java y ahora tiene soporte para XML desde la versi贸n 2.
    • DOM4J es una biblioteca de memoria eficiente para analizar XML, XPath y XSLT (Lenguaje de hoja de estilo extensible).
    • JDom que es una biblioteca de an谩lisis XML con soporte para XPath y XSLT.

    驴Qu茅 es Jackson?

    El proyecto Jackson es una colecci贸n de herramientas de procesamiento de datos para el lenguaje Java y la plataforma JVM. Admite una amplia gama de formatos de datos como CSV, propiedades de Java, XML y YAML a trav茅s de componentes de extensi贸n que admiten el lenguaje espec铆fico.

    El componente XML de Jackson est谩 dise帽ado para leer y escribir datos XML emulando c贸mo funciona JAXB, aunque no de manera concluyente.

    En este art铆culo usaremos la biblioteca Jackson para serializar objetos Java en XML y deserializarlos nuevamente en objetos Java.

    Configuraci贸n del proyecto

    Primero, configuremos un nuevo proyecto de Maven:

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

    Con nuestro proyecto generado, agreguemos la dependencia de Jackson en nuestro pom.xml archivo. Elimine la secci贸n de dependencias existentes y reempl谩cela con:

    <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.Pharos.sh.App</mainClass>
                  </transformer>
                </transformers>
              </configuration>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
    

    Ahora podemos probar el proyecto que hemos configurado ejecutando los siguientes comandos:

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

    La salida debe ser Hello World! impreso en nuestra terminal mostrando que nuestro proyecto est谩 listo para el siguiente paso del proyecto.

    Serializaci贸n de objetos Java en XML

    Los objetos Java tienen atributos y m茅todos para manipular estos atributos. En relaci贸n con un documento XML, los elementos del documento se pueden asignar a atributos de un objeto Java.

    En el proceso de serializaci贸n, los atributos de un objeto se convierten en elementos XML y se almacenan en un documento XML.

    Usaremos un PhoneDetails clase que definir谩 informaci贸n sobre un modelo de tel茅fono en particular, como su nombre, tama帽o de pantalla y capacidad de almacenamiento interno. En nuestra clase, estos ser谩n atributos, pero en nuestro documento XML, estos detalles estar谩n contenidos en etiquetas o elementos.

    Empecemos por definir el PhoneDetails clase que se utilizar谩 para generar nuestros objetos:

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

    Con nuestro conjunto de objetos, modifiquemos nuestro App.java y agregue una funci贸n para manejar la serializaci贸n a 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();
    }
    

    Empaquetemos y ejecutemos nuestro proyecto una vez m谩s:

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

    La salida en el terminal es:

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

    En la carpeta ra铆z de nuestro proyecto, el serialized.xml Se crea un archivo que contiene esta informaci贸n. Hemos serializado con 茅xito nuestro objeto Java en XML y lo hemos escrito en un archivo XML.

    En nuestro serializeToXML() funci贸n, creamos una XmlMapper objeto, que es una clase secundaria para el ObjectMapper clase utilizada en la serializaci贸n JSON. Esta clase convierte nuestro objeto Java en una salida XML que ahora podemos escribir en un archivo.

    Deserializaci贸n de XML

    Jackson tambi茅n nos permite leer el contenido de un archivo XML y deserializar la cadena XML nuevamente en un objeto Java. En nuestro ejemplo, leeremos un documento XML que contiene detalles sobre un tel茅fono y usaremos Jackson para extraer estos datos y usarlos para crear objetos Java que contengan la misma informaci贸n.

    Primero, creemos un documento XML que coincida con nuestra clase para leer. Crear to_deserialize.xml con el siguiente contenido:

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

    Agreguemos un deserializeFromXML() funci贸n para deserializar el archivo XML anterior en un objeto 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();
    }
    

    Empaquetamos y ejecutamos nuestro proyecto como de costumbre y el resultado es:

    Deserializing from XML...
    
    Deserialized data:
        Name: iPhone
        Memory: 3/64 GB
        Display Size: 6.2
    

    Nuestro archivo XML se ha deserializado con 茅xito y todos los datos se han extra铆do con la ayuda de la biblioteca Jackson.

    Anotaciones de Jackson

    Las anotaciones se utilizan para agregar metadatos a nuestro c贸digo Java y no tienen ning煤n efecto directo en la ejecuci贸n del c贸digo al que est谩n adjuntos. Se utilizan para dar instrucciones al compilador durante el tiempo de compilaci贸n y el tiempo de ejecuci贸n.

    Jackson usa anotaciones para varias funciones, como definir si estamos mapeando a XML o JSON, definir el orden de atributos y campos en nuestra salida o sus nombres.

    Estas anotaciones generalmente se aplican en nuestros POJOs de Java (Objetos Java antiguos simples). Por ejemplo, podemos anotar nuestro PhoneDetails clase de la siguiente manera:

    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
    }
    

    los @JsonProperty La anotaci贸n ayuda a definir el nombre de los campos en nuestro archivo XML. Con esta anotaci贸n agregada, las etiquetas en nuestros archivos XML de entrada y salida tendr谩n que parecerse a las cadenas de la anotaci贸n de la siguiente manera:

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

    Otra anotaci贸n notable es la @JacksonXmlText que indica que un elemento debe mostrarse como texto sin formato sin etiquetas u otro elemento que lo contenga.

    los @JacksonXmlProperty La anotaci贸n se puede utilizar para controlar los detalles del atributo o elemento que se muestra. Dichos detalles pueden incluir el espacio de nombres del elemento. Los espacios de nombres son una forma de asignar elementos a un grupo en particular.

    Un uso principal de los espacios de nombres es evitar conflictos cuando se utilizan etiquetas similares en el documento, ayudan a aislar las etiquetas de un grupo para eliminar cualquier ambig眉edad que pueda surgir a medida que los documentos XML escalan.

    El orden de las propiedades tambi茅n se puede especificar mediante un @JsonPropertyOrder anotaci贸n. Por ejemplo, para invertir el orden de los elementos en la salida del documento XML, la anotaci贸n se usa de la siguiente manera:

    @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;
    
        ...
    

    La salida de la serializaci贸n a XML ahora ser谩:

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

    Si hay campos en los objetos Java que no deseamos serializar, podemos usar el @JsonIgnore anotaci贸n y los campos se omitir谩n durante la serializaci贸n y deserializaci贸n.

    Las anotaciones de Jackson son 煤tiles para definir y controlar el proceso de serializaci贸n y deserializaci贸n en varios formatos como XML, JSON y YAML. Algunas anotaciones funcionan para todos los formatos y algunas est谩n vinculadas a un tipo espec铆fico de archivo.

    Se pueden encontrar m谩s anotaciones de Jackson y sus usos en este wiki oficial en Github.

    Manipulaci贸n de listas y elementos anidados en XML

    Habiendo aprendido sobre las anotaciones, mejoremos nuestro archivo XML para agregar elementos y bucles anidados y modifiquemos nuestro c贸digo para serializar y deserializar la siguiente estructura actualizada:

    <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>
    

    En esta nueva estructura, hemos introducido un anidado Manufacturer elemento que tambi茅n incluye una lista de elementos. Con nuestro c贸digo actual, no podemos extraer ni crear la nueva secci贸n anidada.

    Para solucionar esto, se requiere una nueva clase para manejar el elemento anidado, y para ese efecto, esto es parte de nuestra nueva Manufacturer clase:

    // 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;
    
        ...
    

    Es bastante similar a nuestro PhoneDetails class pero ahora hemos introducido una nueva anotaci贸n: @JacksonXmlElementWrapper. El prop贸sito de esta anotaci贸n es definir si una colecci贸n de elementos usa o no un elemento contenedor, y puede usarse para dictar el nombre local y el espacio de nombres de los elementos contenedor.

    En nuestro ejemplo, usamos la anotaci贸n para definir el elemento que contiene una lista de elementos y la etiqueta que se usar谩 para ese elemento. Esto se utilizar谩 al serializar y deserializar nuestros archivos XML.

    Este cambio en nuestra estructura XML y la introducci贸n de esta clase requiere que modifiquemos nuestra PhoneDetails clase para reflexionar:

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

    Nuestro PhoneDetails El objeto ahora podr谩 incluir informaci贸n sobre el fabricante de un tel茅fono.

    A continuaci贸n, actualizamos nuestro serializeToXML() m茅todo:

    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
        }
    }
    

    El resultado de serializar el nuevo PhoneDetails objeto con el Manufacturer la informaci贸n es:

    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>
    

    隆Funciona! Nuestro deserializeFromXML() La funci贸n, por otro lado, no necesita una actualizaci贸n importante ya que el PhoneDetails class, cuando se deserializa, tambi茅n incluir谩 informaci贸n del fabricante.

    Agreguemos el siguiente c贸digo para imprimir los detalles del fabricante solo para estar seguros:

    // 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
    

    La salida:

    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]
    

    El proceso de deserializaci贸n es perfecto y los nuevos detalles del fabricante se han extra铆do de nuestro archivo XML actualizado.

    Conclusi贸n

    En esta publicaci贸n, hemos aprendido sobre XML y c贸mo serializar datos en documentos XML, as铆 como deserializar para extraer datos de documentos XML.

    Tambi茅n hemos aprendido acerca de las anotaciones y c贸mo Jackson las usa en el proceso de serializaci贸n y deserializaci贸n.

    XML todav铆a se usa ampliamente en varios sistemas con los que podemos interactuar de vez en cuando, por lo tanto, para interactuar con ellos, necesitaremos serializar y deserializar documentos XML de vez en cuando. Tambi茅n podemos consumir API XML en nuestros proyectos Java mientras exponemos puntos finales REST y usamos Jackson para convertir la entrada XML en salida JSON.

    El c贸digo fuente de esta publicaci贸n est谩 disponible en Github para referencia.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *