JavaRush /Blog Java /Random-ES /Conceptos básicos de XML para programadores Java. Parte 3...
Ярослав
Nivel 40
Днепр

Conceptos básicos de XML para programadores Java. Parte 3.2 de 3 - DOM

Publicado en el grupo Random-ES
<h2>Introducción</h2>Hola a todos los lectores del artículo, esta parte está dedicada al DOM. El próximo estará dedicado a JAXB y, con ello, se completará el ciclo de conceptos básicos de XML. Primero habrá un poco de teoría y luego solo práctica. Empecemos. <h2>DOM (Document Object Model) - TEORÍA</h2>El controlador DOM está diseñado de tal manera que lee todo el XML a la vez y lo guarda, creando una jerarquía en forma de árbol a través del cual podemos movernos fácilmente. y acceder a los elementos que necesitamos. Por lo tanto, podemos, dado un enlace al elemento superior, obtener todos los enlaces a sus elementos internos. Además, los elementos que están dentro del elemento son hijos de este elemento y es su padre. Una vez que hayamos leído todo el XML en la memoria, simplemente recorreremos su estructura y realizaremos las acciones que necesitemos. Un poco sobre la parte de programación del DOM en Java: el DOM tiene muchas interfaces que se crean para describir diferentes datos. Todas estas interfaces heredan una interfaz común: Node. Porque, de hecho, el tipo de datos más común en el DOM es Nodo, que puede ser cualquier cosa. Cada Nodo tiene los siguientes métodos útiles para recuperar información:
  1. getNodeName– obtener el nombre del host.
  2. getNodeValue– obtener el valor del nodo.
  3. getNodeType– obtener el tipo de nodo.
  4. getParentNode– obtener el nodo dentro del cual se encuentra el nodo dado.
  5. getChildNodes– obtener todos los nodos derivados (nodos que están dentro de un nodo determinado).
  6. getAttributes– obtener todos los atributos del nodo.
  7. getOwnerDocument– obtener el documento de este nodo.
  8. getFirstChild/getLastChild– obtener el primer/último nodo derivado.
  9. getLocalName– útil al procesar espacios de nombres para obtener un nombre sin prefijo.
  10. getTextContent– devuelve todo el texto dentro de un elemento y todos los elementos dentro de un elemento determinado, incluidos los saltos de línea y los espacios.
Nota sobre el método 9: siempre devolverá nulo a menos que haya utilizado el método setNamespaceAware(true) en DocumentFactory para activar el procesamiento del espacio de nombres. Ahora, un detalle importante: los métodos son comunes a todos los Nodos, pero en Nodo podemos tener tanto un elemento como un atributo. Y aquí vienen las preguntas: ¿qué valor puede tener un elemento? ¿Qué nodos derivados puede tener un atributo? Y otros no son consistentes. Y todo es bastante sencillo: cada método funcionará dependiendo del tipo de Nodo . Por supuesto, basta con utilizar la lógica para no confundirse. Por ejemplo: ¿qué atributos pueden tener los atributos? ¿Qué otro significado tiene el elemento? Sin embargo, para no intentarlo todo usted mismo, en los documentos oficiales hay una tabla muy útil sobre cómo funciona cada método dependiendo del tipo de Nodo: La calidad resultó ser mala, así que aquí hay un enlace a la documentación (tabla en en la parte superior de la página): Documentación del nodo Lo más importante que debe recordar:
  1. SÓLO los elementos tienen atributos.
  2. Los elementos NO tienen significado.
  3. El nombre del nodo del elemento es el mismo que el nombre de la etiqueta y el nombre del nodo del atributo es el mismo que el nombre del atributo.
<h2>DOM (Modelo de Objetos de Documento) - PRÁCTICA</h2>En la parte práctica analizaremos varios tipos de tareas de búsqueda de información en XML. También tomamos dos tareas del artículo anterior para comparar la conveniencia. Comencemos, sería bueno comenzar con las importaciones:
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
Proporciono importaciones para que no confunda las clases :) Tarea número 1: necesitamos obtener información sobre todos los empleados y enviarla a la consola desde el siguiente archivo XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
    <name>IT-Heaven</name>
    <offices>
        <office floor="1" room="1">
            <employees>
                <employee name="Maksim" job="Middle Software Developer" />
                <employee name="Ivan" job="Junior Software Developer" />
                <employee name="Franklin" job="Junior Software Developer" />
            </employees>
        </office>
        <office floor="1" room="2">
            <employees>
                <employee name="Herald" job="Middle Software Developer" />
                <employee name="Adam" job="Middle Software Developer" />
                <employee name="Leroy" job="Junior Software Developer" />
            </employees>
        </office>
    </offices>
</company>
Como vemos, tenemos toda la información almacenada en los elementos de empleado. Para almacenarlo en algún lugar de nuestro programa, creemos una clase Employee:
public class Employee {
    private String name, job;

    public Employee(String name, String job) {
        this.name = name;
        this.job = job;
    }

    public String getName() {
        return name;
    }

    public String getJob() {
        return job;
    }
}
Ahora que tenemos una descripción de la estructura para almacenar datos, necesitamos una colección que almacene a los empleados. Lo crearemos en el propio código. También necesitamos crear un Documento basado en nuestro XML:
public class DOMExample {
    // Список для сотрудников из XML archivo
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Получение фабрики, чтобы после получить билдер documentoов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // Получo из фабрики билдер, который парсит XML, создает структуру Document в виде иерархического дерева.
        DocumentBuilder builder = factory.newDocumentBuilder();

        // Запарсo XML, создав структуру Document. Теперь у нас есть доступ ко всем elementoм, Cómoим нам нужно.
        Document document = builder.parse(new File("resource/xml_file1.xml"));
    }
}
Una vez que recibimos el documento, tenemos poder ilimitado sobre toda la estructura del archivo XML. Podemos recuperar cualquier elemento en cualquier momento, volver atrás para comprobar cualquier dato y, en general, un enfoque más flexible que el que teníamos en SAX. En el contexto de esta tarea, sólo necesitamos extraer todos los elementos de los empleados y luego extraer toda la información sobre ellos. Es bastante simple:
public class DOMExample {
    // Список для сотрудников из XML archivo
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Получение фабрики, чтобы после получить билдер documentoов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // Получo из фабрики билдер, который парсит XML, создает структуру Document в виде иерархического дерева.
        DocumentBuilder builder = factory.newDocumentBuilder();

        // Запарсo XML, создав структуру Document. Теперь у нас есть доступ ко всем elementoм, Cómoим нам нужно.
        Document document = builder.parse(new File("resource/xml_file1.xml"));

        // Получение списка всех элементов employee внутри корневого elemento (getDocumentElement возвращает ROOT элемент XML archivo).
        NodeList employeeElements = document.getDocumentElement().getElementsByTagName("employee");

        // Перебор всех элементов employee
        for (int i = 0; i < employeeElements.getLength(); i++) {
            Node employee = employeeElements.item(i);

            // Получение атрибутов каждого elemento
            NamedNodeMap attributes = employee.getAttributes();

            // Добавление сотрудника. Атрибут - тоже Node, потому нам нужно получить significado атрибута с помощью метода getNodeValue()
            employees.add(new Employee(attributes.getNamedItem("name").getNodeValue(), attributes.getNamedItem("job").getNodeValue()));
        }

        // Вывод информации о каждом сотруднике
        for (Employee employee : employees)
            System.out.println(String.format("Информации о сотруднике: Nombre - %s, должность - %s.", employee.getName(), employee.getJob()));
    }
}
La descripción de esta solución está justo en la solución. Es recomendable, después de ver el código, volver a la teoría y leerla nuevamente. De hecho, todo está claro instintivamente. Lea los comentarios con atención y no debería haber ninguna pregunta, y si hay alguna, puede escribir en los comentarios, yo responderé, o en el enlace, o simplemente ejecutar su IDEA e intentar jugar con el código usted mismo si aún no lo has hecho. Entonces, después de ejecutar el código obtuvimos el siguiente resultado:
Информации о сотруднике: Nombre - Maksim, должность - Middle Software Developer.
Информации о сотруднике: Nombre - Ivan, должность - Junior Software Developer.
Информации о сотруднике: Nombre - Franklin, должность - Junior Software Developer.
Информации о сотруднике: Nombre - Herald, должность - Middle Software Developer.
Информации о сотруднике: Nombre - Adam, должность - Middle Software Developer.
Информации о сотруднике: Nombre - Leroy, должность - Junior Software Developer.
Como puede ver, ¡la tarea se completó con éxito! Pasemos a la siguiente tarea :) Tarea No. 2: el nombre de un elemento se ingresa desde la consola, sobre el cual debe mostrar información sobre todos los elementos que contiene y sus atributos del siguiente archivo XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <oracle>
        <connection value="jdbc:oracle:thin:@10.220.140.48:1521:test1" />
        <user value="secretOracleUsername" />
        <password value="111" />
    </oracle>

    <mysql>
        <connection value="jdbc:mysql:thin:@10.220.140.48:1521:test1" />
        <user value="secretMySQLUsername" />
        <password value="222" />
    </mysql>
</root>
Todo es bastante simple: debemos obtener el elemento por su nombre, que contamos, y luego recorrer todos los nodos secundarios. Para hacer esto, debe recorrer todos los nodos secundarios de todos los nodos secundarios que son elementos. Solución a este problema:
public class DOMExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Ридер для считывания имени тега из консоли
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        // Получение фабрики, чтобы после получить билдер documentoов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // Получo из фабрики билдер, который парсит XML, создает структуру Document в виде иерархического дерева.
        DocumentBuilder builder = factory.newDocumentBuilder();

        // Запарсo XML, создав структуру Document. Теперь у нас есть доступ ко всем elementoм, Cómoим нам нужно.
        Document document = builder.parse(new File("resource/xml_file3.xml"));

        // Считывание имени тега для поиска его в файле
        String element = reader.readLine();

        // Получение списка элементов, однако для удобства будем рассматривать только первое совпадение в documentoе.
        // Так же заметьте, что мы ищем элемент внутри documentoа, а не рут elemento. Это сделано для того, чтобы рут элемент тоже искался.
        NodeList matchedElementsList = document.getElementsByTagName(element);

        // Даже если elemento нет, всегда будет возвращаться список, просто он будет пустым.
        // Потому, чтобы утверждать, что elemento нет в файле, достаточно проверить размер списка.
        if (matchedElementsList.getLength() == 0) {
            System.out.println("Etiqueta " + element + " не был найден в файле.");
        } else {
            // Получение первого elemento.
            Node foundedElement = matchedElementsList.item(0);

            System.out.println("Элемент был найден!");

            // Если есть данные внутри, вызов метода для вывода всей информации
            if (foundedElement.hasChildNodes())
                printInfoAboutAllChildNodes(foundedElement.getChildNodes());
        }
    }

    /**
     * Рекурсивный метод, который будет выводить информацию про все узлы внутри всех узлов, которые пришли параметром, пока не будут перебраны все узлы.
     * @param list Список узлов.
     */
    private static void printInfoAboutAllChildNodes(NodeList list) {
        for (int i = 0; i < list.getLength(); i++) {
            Node node = list.item(i);

            // У элементов есть два вида узлов - другие элементы o текстовая информация. Потому нужно разбираться две ситуации отдельно.
            if (node.getNodeType() == Node.TEXT_NODE) {
                // Фильтрация информации, так Cómo пробелы и переносы строчек нам не нужны. Это не информация.
                String textInformation = node.getNodeValue().replace("\n", "").trim();

                if(!textInformation.isEmpty())
                    System.out.println("Внутри elemento найден текст: " + node.getNodeValue());
            }
            // Если это не текст, а элемент, то обрабатываем его Cómo элемент.
            else {
                System.out.println("Найден элемент: " + node.getNodeName() + ", его атрибуты:");

                // Получение атрибутов
                NamedNodeMap attributes = node.getAttributes();

                // Вывод информации про все атрибуты
                for (int k = 0; k < attributes.getLength(); k++)
                    System.out.println("Nombre атрибута: " + attributes.item(k).getNodeName() + ", его significado: " + attributes.item(k).getNodeValue());
            }

            // Если у данного elemento еще остались узлы, то вывести всю информацию про все его узлы.
            if (node.hasChildNodes())
                printInfoAboutAllChildNodes(node.getChildNodes());
        }
    }
}
La descripción completa de la solución está en los comentarios, pero me gustaría ilustrar un poco gráficamente el enfoque que utilizamos, usando un ejemplo de una imagen de la teoría. Asumiremos que necesitamos mostrar información sobre la etiqueta html. Como puede ver, debemos ir de arriba a abajo desde la raíz del árbol. Todas las líneas son nodos. En la solución, iremos recursivamente desde el principio del elemento deseado a través de todos sus nodos, y si uno de sus nodos es un elemento, también recorreremos todos los nodos de este elemento. Entonces, después de ejecutar el código obtuvimos el siguiente resultado para el elemento raíz:
Элемент был найден!
Найден элемент: oracle, его атрибуты:
Найден элемент: connection, его атрибуты:
Nombre атрибута: value, его significado: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Nombre атрибута: value, его significado: secretOracleUsername
Найден элемент: password, его атрибуты:
Nombre атрибута: value, его significado: 111
Найден элемент: mysql, его атрибуты:
Найден элемент: connection, его атрибуты:
Nombre атрибута: value, его significado: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Nombre атрибута: value, его significado: secretMySQLUsername
Найден элемент: password, его атрибуты:
Nombre атрибута: value, его significado: 222
¡El problema se ha resuelto con éxito! Tarea No. 3: del siguiente archivo XML, donde se guarda información sobre estudiantes, profesores y empleados, debe leer la información y enviarla a la consola:
<?xml version="1.0" encoding="UTF-8"?>
<database>
    <students>
        <student name="Maksim" course="3" specialization="CE" />
        <student name="Stephan" course="1" specialization="CS" />
        <student name="Irvin" course="2" specialization="CE" />
    </students>

    <professors>
        <professor name="Herald" experience="7 years in University" discipline="Math" />
        <professor name="Adam" experience="4 years in University" discipline="Programming" />
        <professor name="Anton" experience="6 years in University" discipline="English" />
    </professors>

    <service>
        <member name="John" position="janitor" />
        <member name="Jordan" position="janitor" />
        <member name="Mike" position="janitor" />
    </service>
</database>
La tarea es bastante sencilla, pero interesante. Primero, necesitamos crear 4 clases: empleado, profesor y estudiante, así como una clase abstracta común Humano para poder llevar el nombre variable de cada clase bajo un denominador común: Clase padre abstracta
public abstract class Human {
    private String name;

    public Human(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
Alumno
public class Student extends Human {
    private String course, specialization;

    public Student(String name, String course, String specialization) {
        super(name);
        this.course = course;
        this.specialization = specialization;
    }

    public String getCourse() {
        return course;
    }

    public String getSpecialization() {
        return specialization;
    }

    public String toString() {
        return "Голодный студент " + getName() + " " + course + "-го курса, обучающийся по специальности " + specialization;
    }
}
Profesor
public class Professor extends Human {
    private String experience, discipline;

    public Professor(String name, String experience, String discipline) {
        super(name);
        this.experience = experience;
        this.discipline = discipline;
    }

    public String getExperience() {
        return experience;
    }

    public String getDiscipline() {
        return discipline;
    }

    public String toString() {
        return "Профессор " + getName() + ", обладающий опытом: \"" + experience + "\", выкладает дисциплину " + discipline;
    }
}
Empleado
public class Member extends Human {
    private String position;

    public Member(String name, String position) {
        super(name);
        this.position = position;
    }

    public String getPosition() {
        return position;
    }

    public String toString() {
        return "Сотрудник обслуживающего персонала " + getName() + ", должность: " + position;
    }
}
Ahora que nuestras clases están listas, solo necesitamos escribir código para obtener todos los elementos estudiante, profesor y miembro, y luego obtener sus atributos. Para el almacenamiento, usaremos una colección que almacenará objetos de la clase principal común a todos: Humano. Y así, la solución a este problema:
public class DOMExample {
    // Коллекция для хранения всех людей
    private static ArrayList<Human> humans = new ArrayList<>();

    // Константы для элементов
    private static final String PROFESSOR = "professor";
    private static final String MEMBER = "member";
    private static final String STUDENT = "student";

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Получение фабрики, чтобы после получить билдер documentoов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // Получo из фабрики билдер, который парсит XML, создает структуру Document в виде иерархического дерева.
        DocumentBuilder builder = factory.newDocumentBuilder();

        // Запарсo XML, создав структуру Document. Теперь у нас есть доступ ко всем elementoм, Cómoим нам нужно.
        Document document = builder.parse(new File("resource/xml_file3.xml"));

        // Получение информации про каждый элемент отдельно
        collectInformation(document, PROFESSOR);
        collectInformation(document, MEMBER);
        collectInformation(document, STUDENT);

        // Вывод информации
        humans.forEach(System.out::println);
    }

    /**
     * Метод ищет информацию про теги по имени element и вносит всю информацию в коллекцию humans.
     * @param document Документ, в котором будем искать элементы.
     * @param element Nombre elemento, теги которого нужно найти. Должна быть одна из констант, которые определяются выше.
     */
    private static void collectInformation(Document document, final String element) {
        // Получение всех элементов по имени тега.
        NodeList elements = document.getElementsByTagName(element);

        // Перебор всех найденных элементов
        for (int i = 0; i < elements.getLength(); i++) {
            // Получение всех атрибутов elemento
            NamedNodeMap attributes = elements.item(i).getAttributes();
            String name = attributes.getNamedItem("name").getNodeValue();

            // В зависимости от типа elemento, нам нужно собрать свою дополнительну информацию про каждый подкласс, а после добавить нужные образцы в коллекцию.
            switch (element) {
                case PROFESSOR: {
                    String experience = attributes.getNamedItem("experience").getNodeValue();
                    String discipline = attributes.getNamedItem("discipline").getNodeValue();

                    humans.add(new Professor(name, experience, discipline));
                } break;
                case STUDENT: {
                    String course = attributes.getNamedItem("course").getNodeValue();
                    String specialization = attributes.getNamedItem("specialization").getNodeValue();

                    humans.add(new Student(name, course, specialization));
                } break;
                case MEMBER: {
                    String position = attributes.getNamedItem("position").getNodeValue();

                    humans.add(new Member(name, position));
                } break;
            }
        }
    }
}
Tenga en cuenta que solo necesitamos el nombre del elemento para obtener todos estos elementos del documento. Esto simplifica enormemente el proceso de encontrar la información que necesita. Toda la información sobre el código está incluida en los comentarios. No se utilizó nada nuevo que no estuviera presente en tareas anteriores. Salida de código:
Профессор Herald, обладающий опытом: "7 years in University", выкладает дисциплину Math
Профессор Adam, обладающий опытом: "4 years in University", выкладает дисциплину Programming
Профессор Anton, обладающий опытом: "6 years in University", выкладает дисциплину English
Сотрудник обслуживающего персонала John, должность: janitor
Сотрудник обслуживающего персонала Jordan, должность: janitor
Сотрудник обслуживающего персонала Mike, должность: janitor
Голодный студент Maksim 3-го курса, обучающийся по специальности CE
Голодный студент Stephan 1-го курса, обучающийся по специальности CS
Голодный студент Irvin 2-го курса, обучающийся по специальности CE
¡Problema resuelto! Recomendaciones sobre cuándo usar DOM y cuándo usar SAX La diferencia entre estas herramientas está en la funcionalidad y la velocidad. Si necesita una funcionalidad más flexible y puede darse el lujo de desperdiciar el rendimiento del programa, entonces su elección es DOM, pero si su objetivo principal es reducir los costos de memoria, entonces DOM no es la mejor opción, ya que lee toda la información del archivo XML y lo almacena. Por tanto, el método de lectura secuencial SAX es menos costoso. Brevemente: si necesita rendimiento - SAX, funcionalidad - DOM. <h2>Conclusión</h2>Cada programador tiene sus propias herramientas y, dependiendo de la tarea, es necesario utilizar determinadas herramientas. En los artículos sobre SAX y DOM, mi objetivo era enseñarte cómo extraer información de archivos XML y procesarlos de la forma que necesites. Sin embargo, incluso si ha leído estos artículos, no puede afirmar haber aprendido a utilizar estas herramientas. Debes practicar, probar el código de los artículos, entender cómo funciona e intentar escribir algo tú mismo. Al fin y al cabo, lo más importante es la práctica. El último artículo se publicará en los próximos días y, al parecer, una vez finalizado el concurso, y estará dedicado a JAXB. JAXB es una herramienta para guardar objetos en su programa en formato XML. Eso es todo, espero que este artículo te haya sido útil y buena suerte en tu programación :) Artículo anterior: [Concurso] Conceptos básicos de XML para un programador de Java - Parte 3.1 de 3 - SAX
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION