Breaking

Post Top Ad

Your Ad Spot

viernes, 14 de junio de 2019

Serializar y Deserializar XML en Java con Jackson

Introducción

En un ecosistema de sistemas de software cada vez más conectado, la comunicación entre ellos se ha vuelto aún más importante. A su vez, se han desarrollado varias tecnologías para empaquetar datos que se transfieren o comparten entre estos muchos y diferentes sistemas.
El eXtensible Markup Language , conocido popularmente como XML , es una de las formas de empaquetar datos para ser transferidos. XML es un lenguaje de formato de documento 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 se autodescriben, lo que los hace legibles y fáciles de comprender.
En esta publicación, exploraremos la manipulación XML en Java usando la biblioteca Jackson .

Ventajas y desventajas de XML

XML sigue siendo popular y está en uso en algunos sistemas, ya que tiene algunas ventajas, pero también han surgido nuevas tecnologías para satisfacer algunas de sus deficiencias.
Algunas de las ventajas de XML incluyen:
  • XML no está vinculado a una sola plataforma o lenguaje de programación y se puede usar en muchos sistemas diferentes fácilmente. 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 utilizando una definición de tipo de documento (DTD) o un esquema XML. Este es un conjunto de declaraciones de marcado que definen los bloques de construcción 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.
  • A través de su compatibilidad con HTML, es fácil leer y mostrar los datos contenidos en un documento XML mediante el uso de 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 las nuevas tecnologías incluyen:
  • La sintaxis es bastante redundante y detallada en comparación con otros formatos, como JSON, que es muy breve y directo.
  • Debido a su sintaxis y naturaleza detallada, los documentos XML suelen ser grandes, lo que puede resultar en costos adicionales de almacenamiento y transporte.
  • No tiene soporte para matrices.

Bibliotecas XML

La manipulación de XML en Java puede ser un proceso tedioso, por lo que para facilitar el proceso y acelerar el desarrollo hay varias bibliotecas que podemos usar. Incluyen:
  • Eaxy, que es una biblioteca pequeña y simple para construir, manipular, analizar y buscar XML.
  • La arquitectura de Java para enlace de XML (JAXB) es un marco para asignar clases de Java a representaciones de XML mediante la ordenación de objetos de Java en XML y XML no dominante en objetos de 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 hojas de estilo extensible).
  • JDom, que es una biblioteca de análisis XML con soporte para XPath y XSLT.

Que 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 idioma específico.
El componente XML de Jackson está diseñado para leer y escribir datos XML mediante la emulación de 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 en objetos Java.

Configuración del proyecto

Primero, establezcamos un nuevo proyecto de Maven:
$ mvn archetype:generate -DgroupId=com.stackabuse -DartifactId=xmltutorial -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
Con nuestro proyecto generado, agreguemos la dependencia de Jackson en nuestro pom.xmlarchivo. Eliminar la sección de dependencias existentes y reemplazarla 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.stackabuse.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 Hello World!imprimirse en nuestro terminal que muestre 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 pueden asignarse a los 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 una PhoneDetailsclase 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.
Comencemos por definir la PhoneDetailsclase que se usará 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.javay agreguemos 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();
}
Vamos a empaquetar y ejecutar 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.xmlarchivo se crea con esta información. Hemos serializado con éxito nuestro objeto Java en XML y lo hemos escrito en un archivo XML.
En nuestra serializeToXML()función, creamos un XmlMapperobjeto, que es una clase secundaria a la ObjectMapperclase 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 desde XML

Jackson también nos permite leer el contenido de un archivo XML y deserializar la cadena XML 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, vamos a crear un documento XML que coincida con nuestra clase para leer. Crea to_deserialize.xmlcon los siguientes contenidos:
<PhoneDetails>  
  <name>iPhone</name>
  <displaySize>6.2</displaySize>
  <memory>3/64 GB</memory>
</PhoneDetails>  
Agreguemos una 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 siempre y la salida 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.

Jackson anotaciones

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 los atributos y campos en nuestra salida o sus nombres.
Estas anotaciones se aplican generalmente en nuestros POJOs de Java (Objetos de Java antiguos simples). Por ejemplo, podemos anotar nuestra PhoneDetailsclase 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
}
La @JsonPropertyanotación ayuda a definir el nombre de los campos en nuestro archivo XML. Con esta anotación agregada, las etiquetas en nuestra salida XML y los archivos de entrada tendrán que parecerse a las cadenas en 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 @JacksonXmlTextque indica que un elemento debe mostrarse como texto plano sin etiquetas u otro elemento que lo contenga.
La @JacksonXmlPropertyanotació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 usan etiquetas similares en el documento, ya que ayudan a aislar las etiquetas de un grupo para eliminar cualquier ambigüedad que pueda surgir a medida que los documentos XML se escalan.
El orden de las propiedades también se puede especificar mediante una @JsonPropertyOrderanotación. Por ejemplo, para invertir el orden de los elementos en la salida del documento XML, la anotación se utiliza 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 que se serialicen, podemos usar la @JsonIgnoreanotación y los campos se omitirán durante la serialización y la 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.
Más anotaciones de Jackson y sus usos se pueden encontrar en este wiki oficial en Github.

Manipulación de elementos anidados y listas en XML

Después de haber 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 Manufacturerelemento anidado que también incluye una lista de elementos. Con nuestro código actual, no podemos extraer o crear la nueva sección anidada.
Para arreglar esto, se requiere una nueva clase para manejar el elemento anidado, y para ese efecto, esto es parte de nuestra nueva Manufacturerclase:
// 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 nuestra PhoneDetailsclase, pero ahora hemos introducido una nueva anotación: @JacksonXmlElementWrapperEl propósito de esta anotación es definir si una colección de elementos usa o no un elemento de envoltura, y se puede usar para dictar el nombre local y el espacio de nombres de los elementos de envoltura.
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 PhoneDetailsclase para reflejar:
// existing code remains
public class PhoneDetails {  
    // existing code remains
    @JsonProperty("manufacturer")
    private Manufacturer manufacturer;

    // standard getters and setters for the new element

    ...
Nuestro PhoneDetailsobjeto 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 PhoneDetailsobjeto con la Manufacturerinformació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! Nuestra deserializeFromXML()función, por otro lado, no necesita una actualización importante, ya que la PhoneDetailsclase, 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 transparente y los detalles del nuevo 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 sobre la deserialización para extraer datos de documentos XML.
También hemos aprendido acerca de las anotaciones y cómo Jackson utiliza las anotaciones 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 las API XML en nuestros proyectos Java al tiempo que exponemos los puntos finales REST y utilizamos Jackson para convertir la entrada XML a la salida JSON.
El código fuente de esta publicación está disponible en Github como referencia.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Post Top Ad

Your Ad Spot

Páginas