Leer y escribir XML en Java

La abreviatura "XML" significa - Lenguaje de marcado extensible. Tiene una estructura de marcado similar a HTML y fue diseñado para almacenar y transportar dat...

¿Qué es XML?

La abreviatura "XML" significa - eXtensible Markup Lidioma. Tiene una estructura de marcado similar a HTML y fue diseñado para almacenar y transportar datos. Define un conjunto de reglas que lo hacen legible tanto por humanos como por máquinas.

A pesar de ser un lenguaje de marcado como HTML, XML se usa comúnmente para intercambiar datos entre servicios web, backends y front-ends, al igual que JSON y se considera su predecesor.

Si estás interesado en leer sobre leer y escribir JSON en Java, ¡ya lo tenemos cubierto!

Es importante tener en cuenta que XML no tiene un conjunto predefinido de etiquetas como HTML, sino que están definidas por el usuario. Es esta flexibilidad la que llevó a la creación de múltiples formatos de documentos como RSS, Atom, SOAP y XHTML. Todos estos formatos son subconjuntos de XML, en esencia.

Veamos un documento XML simple, que replica el mismo objeto que usamos anteriormente con respecto a JSON:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<person>
    <age>31</age>
    <hobbies>
        <element>Football</element>
        <element>Swimming</element>
    </hobbies>
    <isMarried>true</isMarried>
    <kids>
        <person>
            <age>5</age>
            <name>Billy</name>
        </person>
        <person>
            <age>3</age>
            <name>Milly</name>
        </person>
    </kids>
    <name>Benjamin Watson</name>
</person>

La diferencia clave entre XML y JSON es que estamos definiendo este archivo con la versión XML y codificando al principio del documento con una etiqueta <?xml>. Otra diferencia es que cada propiedad de objeto debe incluirse en su propia etiqueta: <edad>31</edad>. Los elementos de la matriz no se pueden especificar sin una etiqueta, por lo tanto, para enumerarlos, los envolvemos con <element>...</element> dentro de <hobbies>...</hobbies> etiqueta.

JAXB

Como XML es un formato basado en texto, puede usar las mismas técnicas para leerlo y escribirlo que cualquier otro archivo de texto.

Java, sin embargo, proporciona una manera conveniente de manipular XML usando el marco llamado Java Architecture para XML Binding, o [JAXB](https:// www.oracle.com/technetwork/articles/javase/index-140168.html) para abreviar. Nos permite mapear objetos Java a documentos XML y viceversa. JAXB se introdujo por primera vez en JDK 1.6 y no está disponible en versiones anteriores.

Como JAXB es un marco JDK estándar, no es necesario incluir dependencias externas al proyecto para JDK 1.6+.

Nota: Sin embargo, si está utilizando Java 9 o superior, debe incluir un parámetro adicional al comando javac. Si está utilizando un IDE como IntelliJ IDEA o Eclipse, busque una configuración de opciones de compilador adicional y asegúrese de que incluya la cadena --add-modules java.xml.bind.

En el caso de IntelliJ IDEA, se encuentra en Preferencias -> Construir, Ejecutar, Implementar -> Compilador -> Menú Compilador de Java.

Si aún recibe errores como Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext incluso después de agregar la opción de compilador adicional, agregue las siguientes dependencias de Maven:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.2.11</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
    <version>2.2.11</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.2.11</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>

Los conceptos básicos de JAXB se denominan Clasificación y desarmado. Como era de esperar, están representados por las clases Marshaller y Unmarshaller.

Marshalling es el proceso de convertir objetos Java en XML, y Unmarshaling es el proceso de convertir XML en objetos Java.

JAXB se configura utilizando anotaciones que se importan del paquete javax.xml.bind.annotations.

Definamos una clase Java que represente a la persona descrita en nuestro documento XML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@XmlRootElement
public class Person {

    public Person(String name, int age, boolean isMarried, List<String> hobbies, List<Person> kids) {
        this.name = name;
        this.age = age;
        this.isMarried = isMarried;
        this.hobbies = hobbies;
        this.kids = kids;
    }

    public Person(String name, int age) {
        this(name, age, false, null, null);
    }

    private String name;
    private Integer age;
    private Boolean isMarried;
    private List<String> hobbies;
    private List<Person> kids;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isMarried() {
        return isMarried;
    }

    @XmlElement(name = "isMarried")
    public void setMarried(boolean married) {
        isMarried = married;
    }

    @XmlElementWrapper(name = "hobbies")
    @XmlElement(name = "element")
    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public List<Person> getKids() {
        return kids;
    }

    @XmlElementWrapper(name = "kids")
    @XmlElement(name = "person")
    public void setKids(List<Person> kids) {
        this.kids = kids;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", isMarried=" + isMarried +
                ", hobbies=" + hobbies +
                ", kids=" + kids +
                '}';
    }
}

@XmlRootElement: asigna una clase o un tipo de enumeración a un elemento XML. Describe el elemento raíz del documento XML y debe especificarse en la declaración de clase Persona.

@XmlElementWrapper: genera un elemento contenedor alrededor de la representación XML, una Lista en nuestro caso. Los elementos de la lista deben especificarse explícitamente mediante la anotación @XMLElement.

@XMLElement: asigna una propiedad de un objeto Java a un elemento XML derivado del nombre de la propiedad. Para especificar un nombre de propiedad XML diferente, lo incluimos como un parámetro de cadena en la declaración de la anotación, es decir, (nombre = "persona").

Desarmado

El ejemplo más simple de la técnica de marshalling requerirá que creemos una instancia JAXBContext, pasando Person.class como el único parámetro de entrada de su constructor.

El unmarshaller se crea llamando al método createUnmarshaller(), y su método unmarshal() genera una instancia de la Persona real.

Asegúrese de usar una conversión de tipos explícita, ya que el método unmarshal devuelve el tipo Objeto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Solution {
    public static void main(String[] args) throws Exception {
        Person person = XMLtoPersonExample("person.xml");
        System.out.println(person);
    }

    private static Person XMLtoPersonExample(String filename) throws Exception {
        File file = new File(filename);
        JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);

        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        return (Person) jaxbUnmarshaller.unmarshal(file);
    }
}

Después de ejecutar este código, debería ver algo como:

1
Person{name='Benjamin Watson', age=31, isMarried=true, hobbies=[Football, Swimming], kids=[Person{name='Billy', age=5, isMarried=null, hobbies=null, kids=null}, Person{name='Milly', age=3, isMarried=null, hobbies=null, kids=null}]}

Clasificación

Para demostrar la capacidad de JAXB para escribir un archivo XML utilizando el objeto Java como fuente, agregaremos el siguiente método:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private static void personToXMLExample(String filename, Person person) throws Exception {
    File file = new File(filename);
    JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);

    Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

    jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    jaxbMarshaller.marshal(person, file);
    jaxbMarshaller.marshal(person, System.out);
}

Es muy similar al ejemplo anterior e incluye la creación de un JAXBContext nuevamente. Esta vez, sin embargo, el proceso irá en la dirección opuesta y la salida XML se escribirá en el archivo y en la consola.

Agregando una invocación de este método como la última cadena en Solution.main() como:

1
personToXMLExample("person-output.xml", person);

y ejecutándolo, obtendremos una desafortunada excepción.

1
2
3
4
Exception in thread "main" java.lang.NullPointerException
    at com.wikihtp.xml.Person.isMarried(Person.java:49)
    at com.wikihtp.xml.Person$JaxbAccessorM_isMarried_setMarried_boolean.get(MethodAccessor_Boolean.java:61)
...

Cometimos un error al establecer el tipo de campo isMarried en la clase contenedora Boolean y el tipo de retorno del captador isMarried() en boolean primitivo, lo que lleva a JAXB a intentar desempaquetar null y lanzando una NullPointerException como resultado de ello.

Una solución rápida y fácil para esto sería alinear esos dos con boolean o Boolean.

Después de solucionar el problema, obtendremos el siguiente resultado tanto en la consola como en el archivo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
    <age>31</age>
    <hobbies>
        <element>Football</element>
        <element>Swimming</element>
    </hobbies>
    <kids>
        <person>
            <age>5</age>
            <name>Billy</name>
        </person>
        <person>
            <age>3</age>
            <name>Milly</name>
        </person>
    </kids>
    <isMarried>true</isMarried>
    <name>Benjamin Watson</name>
</person>

Como vemos, es completamente idéntico al archivo XML original que clasificamos en el objeto persona.

Conclusión

Leer y escribir XML en Java podría lograrse fácilmente utilizando el marco JAXB. Usando anotaciones, definimos las reglas de mapeo entre las clases de Java y los documentos XML que representan sus objetos.

XML a menudo se considera un formato obsoleto que es inferior a JSON. Sin embargo, saber cómo leerlo y escribirlo usando Java es una habilidad útil para cualquier desarrollador de software, ya que muchos de los servicios en la web todavía lo usan y aún no tienen una API JSON. Este también es el caso de muchos formatos de archivo que almacenan datos en archivos con formato XML.

Aunque, en caso de que JSON sea más lo tuyo, te sugiero que leas sobre leer y escribir JSON a Java, ¡también lo tenemos cubierto!

Licensed under CC BY-NC-SA 4.0