JavaRush /Java-Blog /Random-DE /XML-Grundlagen für Java-Programmierer. Teil 3.2 von 3 – D...
Ярослав
Level 40
Днепр

XML-Grundlagen für Java-Programmierer. Teil 3.2 von 3 – DOM

Veröffentlicht in der Gruppe Random-DE
<h2>Einleitung</h2>Hallo an alle Leser des Artikels, dieser Teil ist dem DOM gewidmet. Der nächste ist JAXB gewidmet und damit ist der Zyklus der XML-Grundlagen abgeschlossen. Zuerst wird es ein wenig Theorie geben und dann nur noch Praxis. Lass uns anfangen. <h2>DOM (Document Object Model) – THEORIE</h2>Der DOM-Handler ist so konzipiert, dass er das gesamte XML auf einmal liest und speichert, wodurch eine Hierarchie in Form eines Baums entsteht, durch den wir uns leicht bewegen können und auf die Elemente zugreifen, die wir benötigen. Somit können wir, wenn wir einen Link zum obersten Element haben, alle Links zu seinen inneren Elementen erhalten. Darüber hinaus sind die Elemente, die sich innerhalb des Elements befinden, die untergeordneten Elemente dieses Elements, und es ist ihr übergeordnetes Element. Sobald wir das gesamte XML in den Speicher eingelesen haben, gehen wir einfach seine Struktur durch und führen die erforderlichen Aktionen aus. Ein wenig über den Programmierteil des DOM in Java: Das DOM verfügt über viele Schnittstellen, die zur Beschreibung verschiedener Daten erstellt werden. Alle diese Schnittstellen erben eine gemeinsame Schnittstelle – Node. Denn tatsächlich ist der häufigste Datentyp im DOM Node, der alles sein kann. Jeder Knoten verfügt über die folgenden nützlichen Methoden zum Abrufen von Informationen:
  1. getNodeName– Holen Sie sich den Hostnamen.
  2. getNodeValue– Holen Sie sich den Knotenwert.
  3. getNodeType– Holen Sie sich den Knotentyp.
  4. getParentNode– Holen Sie sich den Knoten, in dem sich der angegebene Knoten befindet.
  5. getChildNodes– alle abgeleiteten Knoten abrufen (Knoten, die sich innerhalb eines bestimmten Knotens befinden).
  6. getAttributes– alle Knotenattribute abrufen.
  7. getOwnerDocument– Holen Sie sich das Dokument dieses Knotens.
  8. getFirstChild/getLastChild– Holen Sie sich den ersten/letzten abgeleiteten Knoten.
  9. getLocalName– nützlich bei der Verarbeitung von Namespaces, um einen Namen ohne Präfix zu erhalten.
  10. getTextContent– gibt den gesamten Text innerhalb eines Elements und alle Elemente innerhalb eines bestimmten Elements zurück, einschließlich Zeilenumbrüchen und Leerzeichen.
Hinweis zu Methode 9: Sie gibt immer null zurück, es sei denn, Sie haben die Methode setNamespaceAware(true) in der DocumentFactory verwendet, um die Namespace-Verarbeitung auszulösen. Nun ein wichtiges Detail: Die Methoden sind allen Nodes gemeinsam, aber in Node können wir sowohl ein Element als auch ein Attribut haben. Und hier sind die Fragen: Welchen Wert kann ein Element haben? Welche abgeleiteten Knoten kann ein Attribut haben? Und andere sind nicht konsistent. Und alles ist ganz einfach: Jede Methode funktioniert abhängig vom Knotentyp . Es reicht natürlich aus, Logik anzuwenden, um nicht verwirrt zu werden. Zum Beispiel: Welche Attribute können Attribute haben? Welche andere Bedeutung hat das Element? Um jedoch nicht alles selbst auszuprobieren, gibt es in den offiziellen Dokumenten eine sehr nützliche Tabelle, wie die einzelnen Methoden abhängig vom Node-Typ funktionieren: Die Qualität stellte sich als schlecht heraus, daher hier ein Link zur Dokumentation (Tabelle unter). oben auf der Seite): Knotendokumentation Das Wichtigste, das Sie sich merken sollten:
  1. NUR Elemente haben Attribute.
  2. Elemente haben KEINE Bedeutung.
  3. Der Name des Elementknotens ist mit dem Namen des Tags identisch, und der Name des Attributknotens ist mit dem Namen des Attributs identisch.
<h2>DOM (Document Object Model) – PRAXIS</h2>Im praktischen Teil analysieren wir verschiedene Arten von Aufgaben zur Suche nach Informationen in XML. Wir haben auch zwei Aufgaben aus dem vorherigen Artikel übernommen, um den Komfort zu vergleichen. Fangen wir an, und es wäre gut, mit den Importen zu beginnen:
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;
Damit Sie die Klassen nicht verwechseln, stelle ich Importe bereit :) Aufgabe Nr. 1 – Wir müssen Informationen über alle Mitarbeiter abrufen und diese aus der folgenden XML-Datei an die Konsole ausgeben:
<?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>
Wie wir sehen, haben wir alle Informationen in den Mitarbeiterelementen gespeichert. Um es irgendwo in unserem Programm zu speichern, erstellen wir eine Klasse 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;
    }
}
Nachdem wir nun die Struktur zum Speichern von Daten beschrieben haben, benötigen wir eine Sammlung, in der Mitarbeiter gespeichert werden. Wir werden es im Code selbst erstellen. Wir müssen auch ein Dokument basierend auf unserem XML erstellen:
public class DOMExample {
    // Список для сотрудников из XML Datei
    private static ArrayList<Employee> employees = new ArrayList<>();

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

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

        // Запарсoder XML, создав структуру Document. Теперь у нас есть доступ ко всем Elementм, Wieим нам нужно.
        Document document = builder.parse(new File("resource/xml_file1.xml"));
    }
}
Sobald wir das Dokument erhalten, haben wir uneingeschränkte Kontrolle über die gesamte Struktur der XML-Datei. Wir können jederzeit alle Elemente abrufen, alle Daten erneut überprüfen und haben im Allgemeinen einen flexibleren Ansatz als bei SAX. Im Rahmen dieser Aufgabe müssen wir lediglich alle Mitarbeiterelemente extrahieren und dann alle Informationen darüber extrahieren. Es ist ganz einfach:
public class DOMExample {
    // Список для сотрудников из XML Datei
    private static ArrayList<Employee> employees = new ArrayList<>();

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

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

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

        // Получение списка всех элементов employee внутри корневого Element (getDocumentElement возвращает ROOT элемент XML Datei).
        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, потому нам нужно получить Bedeutung атрибута с помощью метода 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()));
    }
}
Die Beschreibung dieser Lösung finden Sie direkt in der Lösung. Es ist ratsam, nach der Betrachtung des Codes zur Theorie zurückzukehren und sie erneut zu lesen. Tatsächlich ist alles instinktiv klar. Lesen Sie die Kommentare sorgfältig durch und es sollte keine Fragen geben, und wenn es welche gibt, können Sie in die Kommentare schreiben, ich werde antworten, oder in den Link, oder einfach Ihre IDEE ausführen und versuchen, selbst mit dem Code zu spielen, wenn Das hast du noch nicht getan. Nachdem wir den Code ausgeführt hatten, erhielten wir die folgende Ausgabe:
Информации о сотруднике: 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.
Wie Sie sehen, wurde die Aufgabe erfolgreich abgeschlossen! Fahren wir mit der nächsten Aufgabe fort :) Aufgabe Nr. 2 – Der Name eines Elements wird über die Konsole eingegeben, über das Sie Informationen zu allen darin enthaltenen Elementen und ihren Attributen aus der folgenden XML-Datei anzeigen müssen:
<?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>
Alles ist ganz einfach: Wir müssen das Element anhand seines Namens ermitteln, den wir zählen, und dann alle untergeordneten Knoten durchgehen. Dazu müssen Sie alle untergeordneten Knoten aller untergeordneten Knoten, die Elemente sind, durchlaufen. Lösung für dieses Problem:
public class DOMExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Ридер для считывания имени тега из консоли
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

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

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

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

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

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

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

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

                if(!textInformation.isEmpty())
                    System.out.println("Внутри Element найден текст: " + node.getNodeValue());
            }
            // Если это не текст, а элемент, то обрабатываем его Wie элемент.
            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() + ", его Bedeutung: " + attributes.item(k).getNodeValue());
            }

            // Если у данного Element еще остались узлы, то вывести всю информацию про все его узлы.
            if (node.hasChildNodes())
                printInfoAboutAllChildNodes(node.getChildNodes());
        }
    }
}
Die gesamte Beschreibung der Lösung steht in den Kommentaren, ich möchte den von uns verwendeten Ansatz jedoch anhand eines Beispiels aus einem Bild aus der Theorie etwas anschaulich veranschaulichen. Wir gehen davon aus, dass wir Informationen zum HTML-Tag anzeigen müssen. Wie Sie sehen, müssen wir von der Wurzel des Baumes von oben nach unten vorgehen. Alle Linien sind Knoten. In der Lösung gehen wir rekursiv vom Anfang des gewünschten Elements durch alle seine Knoten, und wenn einer seiner Knoten ein Element ist, iterieren wir auch durch alle Knoten dieses Elements. Nachdem wir den Code ausgeführt hatten, erhielten wir die folgende Ausgabe für das Stammelement:
Элемент был найден!
Найден элемент: oracle, его атрибуты:
Найден элемент: connection, его атрибуты:
Name атрибута: value, его Bedeutung: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Name атрибута: value, его Bedeutung: secretOracleUsername
Найден элемент: password, его атрибуты:
Name атрибута: value, его Bedeutung: 111
Найден элемент: mysql, его атрибуты:
Найден элемент: connection, его атрибуты:
Name атрибута: value, его Bedeutung: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент: user, его атрибуты:
Name атрибута: value, его Bedeutung: secretMySQLUsername
Найден элемент: password, его атрибуты:
Name атрибута: value, его Bedeutung: 222
Das Problem wurde erfolgreich gelöst! Aufgabe Nr. 3 – Aus der folgenden XML-Datei, in der Informationen über Studenten, Professoren und Mitarbeiter gespeichert sind, müssen Sie die Informationen lesen und an die Konsole ausgeben:
<?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>
Die Aufgabe ist recht einfach, aber interessant. Zuerst müssen wir vier Klassen erstellen: Angestellter, Professor und Student, sowie eine gemeinsame abstrakte Klasse Mensch, um die Namensvariable jeder Klasse auf einen gemeinsamen Nenner zu bringen: abstrakte übergeordnete Klasse
public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
Student
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;
    }
}
Professor
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;
    }
}
Mitarbeiter
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;
    }
}
Da unsere Klassen nun fertig sind, müssen wir nur noch Code schreiben, um alle Elemente „Student“, „Professor“ und „Mitglied“ abzurufen und dann deren Attribute abzurufen. Zur Speicherung verwenden wir eine Sammlung, die Objekte der übergeordneten Klasse speichert, die allen gemeinsam ist – Human. Und so die Lösung für dieses Problem:
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 {
        // Получение фабрики, чтобы после получить билдер dokumentierenов.
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

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

        // Запарсoder XML, создав структуру Document. Теперь у нас есть доступ ко всем Elementм, Wieим нам нужно.
        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;
            }
        }
    }
}
Beachten Sie, dass wir nur den Elementnamen benötigen, um alle diese Elemente aus dem Dokument abzurufen. Dies vereinfacht die Suche nach den benötigten Informationen erheblich. Alle Informationen zum Code finden Sie in den Kommentaren. Es wurde nichts Neues verwendet, was in früheren Aufgaben nicht vorhanden war. Codeausgabe:
Профессор 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
Problem gelöst! Empfehlungen, wann DOM und wann SAX verwendet werden sollten. Der Unterschied zwischen diesen Tools liegt in der Funktionalität und Geschwindigkeit. Wenn Sie eine flexiblere Funktionalität benötigen und es sich leisten können, Programmleistung zu verschwenden, dann ist DOM Ihre Wahl. Wenn Ihr Hauptziel jedoch darin besteht, die Speicherkosten zu senken, ist DOM nicht die beste Wahl, da es alle Informationen aus der XML-Datei liest und speichert es. Daher ist die sequentielle Lesemethode SAX kostengünstiger. Kurz gesagt: Wenn Sie Leistung benötigen – SAX, Funktionalität – DOM. <h2>Fazit</h2>Jeder Programmierer hat seine eigenen Werkzeuge und je nach Aufgabe müssen Sie bestimmte Werkzeuge verwenden. In den Artikeln über SAX und DOM war es mein Ziel, Ihnen beizubringen, wie Sie Informationen aus XML-Dateien extrahieren und sie so verarbeiten, wie Sie sie benötigen. Selbst wenn Sie diese Artikel gelesen haben, können Sie jedoch nicht behaupten, den Umgang mit diesen Tools gelernt zu haben. Sie sollten üben, den Code aus den Artikeln testen, verstehen, wie er funktioniert, und versuchen, selbst etwas zu schreiben. Das Wichtigste ist schließlich die Übung. Der letzte Artikel wird in den kommenden Tagen und offenbar nach Ende des Wettbewerbs veröffentlicht und ist JAXB gewidmet. JAXB ist ein Tool zum Speichern von Objekten in Ihrem Programm im XML-Format. Das ist alles, ich hoffe, dass dieser Artikel nützlich war und wünsche Ihnen viel Glück beim Programmieren :) Vorheriger Artikel: [Wettbewerb] XML-Grundlagen für einen Java-Programmierer – Teil 3.1 von 3 – SAX
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION