JavaRush /Blog Java /Random-FR /Bases XML pour programmeur Java. Partie 3.2 sur 3 - DOM
Ярослав
Niveau 40
Днепр

Bases XML pour programmeur Java. Partie 3.2 sur 3 - DOM

Publié dans le groupe Random-FR
<h2>Introduction</h2>Bonjour à tous les lecteurs de l'article, cette partie est dédiée au DOM. Le prochain sera consacré à JAXB et, ainsi, le cycle des bases de XML sera complété. Il y aura d'abord un peu de théorie, puis seulement de la pratique. Commençons. <h2>DOM (Document Object Model) - THÉORIE</h2>Le gestionnaire DOM est conçu de telle manière qu'il lit tout le XML en même temps et l'enregistre, créant une hiérarchie sous la forme d'un arbre dans lequel nous pouvons facilement nous déplacer et accédez aux éléments dont nous avons besoin . Ainsi, nous pouvons, étant donné un lien vers l'élément supérieur, obtenir tous les liens vers ses éléments internes. De plus, les éléments qui se trouvent à l'intérieur de l'élément sont les enfants de cet élément, et c'est leur parent. Une fois que nous aurons lu tout le XML en mémoire, nous allons simplement parcourir sa structure et effectuer les actions dont nous avons besoin. Un peu sur la partie programmation du DOM en Java : le DOM possède de nombreuses interfaces créées pour décrire différentes données. Toutes ces interfaces héritent d'une interface commune : Node. Parce qu'en fait, le type de données le plus courant dans le DOM est Node, qui peut être n'importe quoi. Chaque nœud dispose des méthodes utiles suivantes pour récupérer des informations :
  1. getNodeName– obtenez le nom d'hôte.
  2. getNodeValue– obtenez la valeur du nœud.
  3. getNodeType– récupère le type de nœud.
  4. getParentNode– récupère le nœud dans lequel se trouve le nœud donné.
  5. getChildNodes– récupère tous les nœuds dérivés (nœuds qui se trouvent à l’intérieur d’un nœud donné).
  6. getAttributes– récupère tous les attributs du nœud.
  7. getOwnerDocument– récupère le document de ce nœud.
  8. getFirstChild/getLastChild– récupère le premier/dernier nœud dérivé.
  9. getLocalName– utile lors du traitement des espaces de noms pour obtenir un nom sans préfixe.
  10. getTextContent– renvoie tout le texte d'un élément et tous les éléments d'un élément donné, y compris les sauts de ligne et les espaces.
Remarque sur la méthode 9 : elle renverra toujours null sauf si vous avez utilisé la méthode setNamespaceAware(true) dans DocumentFactory pour déclencher le traitement de l'espace de noms. Maintenant, un détail important : les méthodes sont communes à tous les Nodes, mais dans Node on peut avoir à la fois un élément et un attribut. Et voici les questions : quelle valeur peut avoir un élément ? Quels nœuds dérivés un attribut peut-il avoir ? Et d'autres ne sont pas cohérents. Et tout est assez simple : chaque méthode fonctionnera en fonction du type de nœud . Il suffit bien sûr d’utiliser la logique pour ne pas se tromper. Par exemple : quels attributs les attributs peuvent-ils avoir ? Quelle autre signification l’élément a-t-il ? Cependant, afin de ne pas tout essayer vous-même, dans la doc officielle il y a un tableau très utile sur le fonctionnement de chaque méthode en fonction du type de Node : La qualité s'est avérée mauvaise, voici donc un lien vers la documentation (tableau à en haut de la page) : Documentation du nœud. La chose la plus importante à retenir :
  1. SEULS les éléments ont des attributs.
  2. Les éléments n’ont AUCUNE signification.
  3. Le nom du nœud d'élément est le même que le nom de la balise et le nom du nœud d'attribut est le même que le nom de l'attribut.
<h2>DOM (Document Object Model) - PRATIQUE</h2>Dans la partie pratique, nous analyserons différents types de tâches de recherche d'informations en XML. Nous avons également pris deux tâches de l'article précédent pour comparer la commodité. Commençons, et ce serait bien de commencer par les importations :
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;
Je propose des importations pour que vous ne confondiez pas les classes :) Tâche n°1 - nous devons obtenir des informations sur tous les employés et les afficher sur la console à partir du fichier XML suivant :
<?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>
Comme nous pouvons le voir, nous avons toutes les informations stockées dans les éléments employé. Afin de le stocker quelque part dans notre programme, créons une classe 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;
    }
}
Maintenant que nous avons une description de la structure de stockage des données, nous avons besoin d'une collection qui stockera les employés. Nous allons le créer dans le code lui-même. Nous devons également créer un document basé sur notre XML :
public class DOMExample {
    // Список для сотрудников из XML file
    private static ArrayList<Employee> employees = new ArrayList<>();

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

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

        // Запарсor XML, создав структуру Document. Теперь у нас есть доступ ко всем elementм, Howим нам нужно.
        Document document = builder.parse(new File("resource/xml_file1.xml"));
    }
}
Une fois que nous recevons le document, nous disposons d’un pouvoir illimité sur toute la structure du fichier XML. Nous pouvons récupérer n'importe quel élément à tout moment, revenir en arrière pour vérifier n'importe quelle donnée et, en général, une approche plus flexible que celle que nous avions dans SAX. Dans le cadre de cette tâche, il suffit d’extraire tous les éléments des salariés, puis d’extraire toutes les informations les concernant. C'est assez simple :
public class DOMExample {
    // Список для сотрудников из XML file
    private static ArrayList<Employee> employees = new ArrayList<>();

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

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

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

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

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

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

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

        // Вывод информации о каждом сотруднике
        for (Employee employee : employees)
            System.out.println(String.format("Информации о сотруднике: Name - %s, должность - %s.", employee.getName(), employee.getJob()));
    }
}
La description de cette solution se trouve directement dans la solution. Il est conseillé, après avoir visionné le code, de revenir à la théorie et de la relire. En fait, tout est clair instinctivement. Lisez attentivement les commentaires et il ne devrait y avoir aucune question, et s'il y en a, vous pouvez écrire dans les commentaires, je répondrai, ou dans le lien, ou simplement exécuter votre IDÉE et essayer de jouer avec le code vous-même si vous ne l'avez pas encore fait. Ainsi, après avoir exécuté le code, nous avons obtenu le résultat suivant :
Информации о сотруднике: Name - Maksim, должность - Middle Software Developer.
Информации о сотруднике: Name - Ivan, должность - Junior Software Developer.
Информации о сотруднике: Name - Franklin, должность - Junior Software Developer.
Информации о сотруднике: Name - Herald, должность - Middle Software Developer.
Информации о сотруднике: Name - Adam, должность - Middle Software Developer.
Информации о сотруднике: Name - Leroy, должность - Junior Software Developer.
Comme vous pouvez le constater, la tâche a été accomplie avec succès ! Passons à la tâche suivante :) Tâche n° 2 - le nom d'un élément est saisi à partir de la console, sur lequel vous devez afficher des informations sur tous les éléments qu'il contient et leurs attributs à partir du fichier XML suivant :
<?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>
Tout est assez simple : il faut récupérer l'élément par son nom, que l'on compte, puis parcourir tous les nœuds enfants. Pour ce faire, vous devez parcourir tous les nœuds enfants de tous les nœuds enfants qui sont des éléments. Solution à ce problème :
public class DOMExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Ридер для считывания имени тега из консоли
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

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

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

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

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

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

        // Даже если element нет, всегда будет возвращаться список, просто он будет пустым.
        // Потому, чтобы утверждать, что element нет в файле, достаточно проверить размер списка.
        if (matchedElementsList.getLength() == 0) {
            System.out.println("Tag " + element + " не был найден в файле.");
        } else {
            // Получение первого element.
            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);

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

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

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

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

            // Если у данного element еще остались узлы, то вывести всю информацию про все его узлы.
            if (node.hasChildNodes())
                printInfoAboutAllChildNodes(node.getChildNodes());
        }
    }
}
La description complète de la solution est dans les commentaires, mais je voudrais illustrer un peu graphiquement l'approche que nous avons utilisée, en utilisant un exemple tiré d'une image de la théorie. Nous supposerons que nous devons afficher des informations sur la balise html. Comme vous pouvez le constater, il faut aller de haut en bas depuis la racine de l’arbre. Toutes les lignes sont des nœuds. Dans la solution, nous allons parcourir récursivement depuis le début de l'élément souhaité à travers tous ses nœuds, et si l'un de ses nœuds est un élément, alors nous parcourons également tous les nœuds de cet élément. Ainsi, après avoir exécuté le code, nous avons obtenu le résultat suivant pour l’élément racine :
Элемент был найден!
Найден элемент: oracle, его атрибуты:
Найден элемент: connection, его атрибуты:
Name атрибута: value, его meaning: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Name атрибута: value, его meaning: secretOracleUsername
Найден элемент: password, его атрибуты:
Name атрибута: value, его meaning: 111
Найден элемент: mysql, его атрибуты:
Найден элемент: connection, его атрибуты:
Name атрибута: value, его meaning: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Name атрибута: value, его meaning: secretMySQLUsername
Найден элемент: password, его атрибуты:
Name атрибута: value, его meaning: 222
Le problème a été résolu avec succès ! Tâche n°3 – à partir du fichier XML suivant, où sont enregistrées les informations sur les étudiants, les professeurs et les employés, vous devez lire les informations et les afficher sur la console :
<?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 tâche est assez simple, mais intéressante. Tout d'abord, nous devons créer 4 classes : employé, professeur et étudiant, ainsi qu'une classe abstraite commune Humain afin de regrouper le nom de la variable de chaque classe sous un dénominateur commun : Classe parent abstraite.
public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
Étudiant
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;
    }
}
Professeur
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;
    }
}
Employé
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;
    }
}
Maintenant que nos classes sont prêtes, il ne nous reste plus qu'à écrire du code pour récupérer tous les éléments étudiant, professeur et membre, puis récupérer leurs attributs. Pour le stockage, nous utiliserons une collection qui stockera les objets de la classe parent commune à tous - Humain. Et donc, la solution à ce problème :
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 {
        // Получение фабрики, чтобы после получить билдер documentов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

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

        // Запарсor XML, создав структуру Document. Теперь у нас есть доступ ко всем elementм, Howим нам нужно.
        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 Name element, теги которого нужно найти. Должна быть одна из констант, которые определяются выше.
     */
    private static void collectInformation(Document document, final String element) {
        // Получение всех элементов по имени тега.
        NodeList elements = document.getElementsByTagName(element);

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

            // В зависимости от типа element, нам нужно собрать свою дополнительну информацию про каждый подкласс, а после добавить нужные образцы в коллекцию.
            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;
            }
        }
    }
}
Notez que nous n'avons besoin que du nom de l'élément pour obtenir tous ces éléments du document. Cela simplifie grandement le processus de recherche des informations dont vous avez besoin. Toutes les informations sur le code sont incluses dans les commentaires. Rien de nouveau n'a été utilisé qui n'était présent dans les tâches précédentes. Sortie du code :
Профессор 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
Problème résolu! Recommandations quand utiliser DOM et quand utiliser SAX La différence entre ces outils réside dans la fonctionnalité et la vitesse. Si vous avez besoin de fonctionnalités plus flexibles et pouvez vous permettre de gaspiller les performances du programme, alors votre choix est DOM, mais si votre objectif principal est de réduire les coûts de mémoire, alors DOM n'est pas le meilleur choix, car il lit toutes les informations du fichier XML et le stocke. La méthode de lecture séquentielle SAX est donc moins coûteuse. En bref : si vous avez besoin de performances - SAX, de fonctionnalités - DOM. <h2>Conclusion</h2>Chaque programmeur possède ses propres outils et, selon la tâche, vous devez utiliser certains outils. Dans les articles sur SAX et DOM, mon objectif était de vous apprendre à extraire des informations de fichiers XML et à les traiter selon vos besoins. Cependant, même si vous avez lu ces articles, vous ne pouvez pas prétendre avoir appris à utiliser ces outils. Vous devez vous entraîner, tester le code des articles, comprendre son fonctionnement et essayer d'écrire quelque chose vous-même. Après tout, le plus important, c’est la pratique. Le dernier article sera publié dans les prochains jours et, apparemment, après la fin du concours, et sera consacré à JAXB. JAXB est un outil permettant de sauvegarder des objets dans votre programme au format XML. C'est tout, j'espère que cet article vous a été utile, et bonne chance dans votre programmation :) Article précédent : [Concours] Bases XML pour un programmeur Java - Partie 3.1 sur 3 - SAX
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION