JavaRush /Java Blog /Random-IT /Nozioni di base su XML per programmatori Java. Parte 3.2 ...
Ярослав
Livello 40
Днепр

Nozioni di base su XML per programmatori Java. Parte 3.2 di 3 - DOM

Pubblicato nel gruppo Random-IT
<h2>Introduzione</h2>Ciao a tutti i lettori dell'articolo, questa parte è dedicata al DOM. Il prossimo sarà dedicato a JAXB e, con questo, si completerà il ciclo dei fondamenti di XML. Prima ci sarà un po' di teoria e poi solo la pratica. Iniziamo. <h2>DOM (Document Object Model) - TEORIA</h2>Il gestore DOM è progettato in modo tale da leggere tutto l'XML contemporaneamente e salvarlo, creando una gerarchia sotto forma di albero attraverso il quale possiamo muoverci facilmente e accedere agli elementi di cui abbiamo bisogno. Quindi, dato un collegamento all'elemento superiore, possiamo ottenere tutti i collegamenti ai suoi elementi interni. Inoltre, gli elementi che si trovano all'interno dell'elemento sono i figli di questo elemento, ed è il loro genitore. Una volta letto tutto l'XML in memoria, viaggeremo semplicemente attraverso la sua struttura ed eseguiremo le azioni necessarie. Un po' sulla parte di programmazione del DOM in Java: il DOM ha molte interfacce create per descrivere vari dati. Tutte queste interfacce ereditano un'interfaccia comune: Node. Perché, in effetti, il tipo di dati più comune nel DOM è Node, che può essere qualsiasi cosa. Ciascun nodo dispone dei seguenti metodi utili per il recupero delle informazioni:
  1. getNodeName– ottenere il nome host.
  2. getNodeValue– ottieni il valore del nodo.
  3. getNodeType– ottieni il tipo di nodo.
  4. getParentNode– ottenere il nodo all'interno del quale si trova il nodo dato.
  5. getChildNodes– ottieni tutti i nodi derivati ​​(nodi che si trovano all'interno di un dato nodo).
  6. getAttributes– ottieni tutti gli attributi del nodo.
  7. getOwnerDocument– ottieni il documento di questo nodo.
  8. getFirstChild/getLastChild– ottieni il primo/ultimo nodo derivato.
  9. getLocalName– utile durante l'elaborazione degli spazi dei nomi per ottenere un nome senza prefisso.
  10. getTextContent– restituisce tutto il testo all'interno di un elemento e tutti gli elementi all'interno di un dato elemento, comprese le interruzioni di riga e gli spazi.
Nota sul metodo 9: restituirà sempre null a meno che tu non abbia utilizzato il metodo setNamespaceAware(true) in DocumentFactory per attivare l'elaborazione dello spazio dei nomi. Ora, un dettaglio importante: i metodi sono comuni a tutti i Nodi, ma in Node possiamo avere sia un elemento che un attributo. Ed ecco le domande: che valore può avere un elemento? Quali nodi derivati ​​può avere un attributo? E altri non sono coerenti. E tutto è abbastanza semplice: ogni metodo funzionerà a seconda del Tipo di nodo . Basta usare la logica, ovviamente, per non confondersi. Ad esempio: quali attributi possono avere gli attributi? Quale altro significato ha l'elemento? Tuttavia, per non provare tutto da soli, nella documentazione ufficiale è presente una tabella molto utile su come funziona ciascun metodo a seconda del tipo di Nodo: La qualità si è rivelata pessima, quindi ecco il link alla documentazione (tabella all'indirizzo nella parte superiore della pagina): Documentazione del nodo La cosa più importante da ricordare:
  1. SOLO gli elementi hanno attributi.
  2. Gli elementi NON hanno significato.
  3. Il nome del nodo elemento è uguale al nome del tag e il nome del nodo attributo è uguale al nome dell'attributo.
<h2>DOM (Document Object Model) - PRATICA</h2>Nella parte pratica, analizzeremo vari tipi di attività relative alla ricerca di informazioni in XML. Abbiamo anche svolto due attività dell'articolo precedente per confrontare la comodità. Cominciamo, e sarebbe bene iniziare con le importazioni:
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;
Fornisco importazioni in modo da non confondere le classi :) Attività n. 1: dobbiamo ottenere informazioni su tutti i dipendenti e inviarle alla console dal seguente file 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>
Come possiamo vedere, abbiamo tutte le informazioni memorizzate negli elementi Employee. Per memorizzarlo da qualche parte nel nostro programma, creiamo una 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;
    }
}
Ora che abbiamo una descrizione della struttura per l'archiviazione dei dati, abbiamo bisogno di una raccolta che memorizzerà i dipendenti. Lo creeremo nel codice stesso. Dobbiamo anche creare un documento basato sul nostro 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"));
    }
}
Una volta ricevuto il documento, abbiamo potere illimitato sull'intera struttura del file XML. Possiamo recuperare qualsiasi elemento in qualsiasi momento, tornare indietro per controllare qualsiasi dato e, in generale, un approccio più flessibile rispetto a SAX. Nel contesto di questa attività, dobbiamo solo estrarre tutti gli elementi relativi ai dipendenti e quindi estrarre tutte le informazioni su di essi. È abbastanza semplice:
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 descrizione di questa soluzione è proprio nella soluzione. Si consiglia, dopo aver visionato il codice, di tornare alla teoria e rileggerlo. In effetti, tutto è chiaro istintivamente. Leggi attentamente i commenti e non dovrebbero esserci domande, e se ce ne sono, puoi scrivere nei commenti, risponderò, o nel link, o semplicemente eseguire la tua IDEA e provare a giocare tu stesso con il codice se non l'hai ancora fatto. Quindi, dopo aver eseguito il codice, abbiamo ottenuto il seguente output:
Информации о сотруднике: 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.
Come puoi vedere, l'attività è stata completata con successo! Passiamo all'attività successiva :) Attività n. 2: il nome di un elemento viene inserito dalla console, di cui è necessario visualizzare le informazioni su tutti gli elementi al suo interno e i loro attributi dal seguente file 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>
Tutto è abbastanza semplice: dobbiamo ottenere l'elemento con il suo nome, che contiamo, e poi passare attraverso tutti i nodi figli. Per fare ciò, è necessario scorrere tutti i nodi figli di tutti i nodi figli che sono elementi. Soluzione a questo problema:
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());
        }
    }
}
L'intera descrizione della soluzione è nei commenti, ma vorrei illustrare un po' graficamente l'approccio che abbiamo utilizzato, utilizzando un esempio tratto da un'immagine della teoria. Partiremo dal presupposto che dobbiamo visualizzare le informazioni sul tag html. Come puoi vedere, dobbiamo andare dall'alto verso il basso dalla radice dell'albero. Tutte le linee sono nodi. Nella soluzione, andremo ricorsivamente dall'inizio dell'elemento desiderato attraverso tutti i suoi nodi e, se uno dei suoi nodi è un elemento, itereremo anche attraverso tutti i nodi di questo elemento. Quindi, dopo aver eseguito il codice, abbiamo ottenuto il seguente output per l'elemento root:
Элемент был найден!
Найден элемент: 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
Il problema è stato risolto con successo! Attività n. 3 – dal seguente file XML, in cui vengono salvate le informazioni su studenti, professori e dipendenti, è necessario leggere le informazioni e inviarle alla 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>
Il compito è abbastanza semplice, ma interessante. Innanzitutto, dobbiamo creare 4 classi: impiegato, professore e studente, oltre a una classe astratta comune Human per portare la variabile nome di ciascuna classe sotto un denominatore comune: Classe genitore astratta
public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
Alunno
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;
    }
}
Professore
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;
    }
}
Dipendente
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;
    }
}
Ora che le nostre classi sono pronte, dobbiamo solo scrivere il codice per ottenere tutti gli elementi studente, professore e membro, e quindi ottenere i loro attributi. Per l'archiviazione, utilizzeremo una raccolta che memorizzerà oggetti della classe genitore comune a tutti: Human. E quindi, la soluzione a questo 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 {
        // Получение фабрики, чтобы после получить билдер 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;
            }
        }
    }
}
Tieni presente che abbiamo bisogno solo del nome dell'elemento per ottenere tutti questi elementi dal documento. Ciò semplifica notevolmente il processo di ricerca delle informazioni di cui hai bisogno. Tutte le informazioni sul codice sono incluse nei commenti. Non è stato utilizzato nulla di nuovo che non fosse presente nelle attività precedenti. Uscita del codice:
Профессор 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 risolto! Consigli su quando utilizzare DOM e quando utilizzare SAX La differenza tra questi strumenti sta nella funzionalità e nella velocità. Se hai bisogno di funzionalità più flessibili e puoi permetterti di sprecare le prestazioni del programma, allora la tua scelta è DOM, ma se il tuo obiettivo principale è ridurre i costi di memoria, allora DOM non è la scelta migliore, poiché legge tutte le informazioni dal file XML e lo memorizza. Pertanto il metodo di lettura sequenziale SAX è meno costoso. In breve: se hai bisogno di prestazioni - SAX, funzionalità - DOM. <h2>Conclusione</h2>Ogni programmatore ha i propri strumenti e, a seconda del compito, è necessario utilizzare determinati strumenti. Negli articoli su SAX e DOM, il mio obiettivo era insegnarti come estrarre informazioni da file XML ed elaborarle nel modo in cui ne hai bisogno. Tuttavia, anche se hai letto questi articoli, non puoi affermare di aver imparato ad utilizzare questi strumenti. Dovresti esercitarti, testare il codice degli articoli, capire come funziona e provare a scrivere qualcosa tu stesso. Dopotutto, la cosa più importante è la pratica. L'ultimo articolo sarà pubblicato nei prossimi giorni e, a quanto pare, dopo la fine del concorso, e sarà dedicato a JAXB. JAXB è uno strumento per salvare oggetti nel tuo programma in formato XML. Questo è tutto, spero che questo articolo ti sia stato utile e buona fortuna per la tua programmazione :) Articolo precedente: [Concorso] Nozioni di base su XML per un programmatore Java - Parte 3.1 di 3 - SAX
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION