JavaRush /Blogue Java /Random-PT /Noções básicas de XML para programador Java. Parte 3.2 de...
Ярослав
Nível 40
Днепр

Noções básicas de XML para programador Java. Parte 3.2 de 3 - DOM

Publicado no grupo Random-PT
<h2>Introdução</h2>Olá a todos os leitores do artigo, esta parte é dedicada ao DOM. O próximo será dedicado ao JAXB e, com isso, o ciclo de fundamentos do XML será completado. Primeiro haverá um pouco de teoria e depois apenas prática. Vamos começar. <h2>DOM (Document Object Model) - TEORIA</h2>O manipulador DOM é projetado de forma que lê todo o XML de uma vez e o salva, criando uma hierarquia em forma de árvore pela qual podemos mover facilmente e acesse os elementos que precisamos . Assim, podemos, dado um link para o elemento superior, obter todos os links para seus elementos internos. Além disso, os elementos que estão dentro do elemento são filhos deste elemento, e ele é seu pai. Depois de ler todo o XML na memória, simplesmente percorreremos sua estrutura e executaremos as ações necessárias. Um pouco sobre a parte de programação do DOM em Java: o DOM possui muitas interfaces que são criadas para descrever diferentes dados. Todas essas interfaces herdam uma interface comum – Node. Porque, na verdade, o tipo de dados mais comum no DOM é o Node, que pode ser qualquer coisa. Cada nó possui os seguintes métodos úteis para recuperar informações:
  1. getNodeName– obtenha o nome do host.
  2. getNodeValue– obtenha o valor do nó.
  3. getNodeType– obtenha o tipo de nó.
  4. getParentNode– obtenha o nó dentro do qual o nó fornecido está localizado.
  5. getChildNodes– obter todos os nós derivados (nós que estão dentro de um determinado nó).
  6. getAttributes– obtenha todos os atributos do nó.
  7. getOwnerDocument– obtenha o documento deste nó.
  8. getFirstChild/getLastChild– obtenha o primeiro/último nó derivado.
  9. getLocalName– útil ao processar namespaces para obter um nome sem prefixo.
  10. getTextContent– retorna todo o texto dentro de um elemento e todos os elementos dentro de um determinado elemento, incluindo quebras de linha e espaços.
Observação sobre o método 9: ele sempre retornará nulo, a menos que você tenha usado o método setNamespaceAware(true) no DocumentFactory para acionar o processamento do namespace. Agora, um detalhe importante: os métodos são comuns a todos os Nodes, mas no Node podemos ter tanto um elemento quanto um atributo. E aqui ficam as questões: que valor um elemento pode ter? Quais nós derivados um atributo pode ter? E outros não são consistentes. E tudo é bem simples: cada método funcionará dependendo do tipo de Node . Basta usar a lógica, claro, para não se confundir. Por exemplo: quais atributos os atributos são capazes de ter? Que outro significado o elemento tem? Porém, para não tentar tudo sozinho, na documentação oficial há uma tabela muito útil de como funciona cada método dependendo do tipo de Node: A qualidade acabou sendo ruim, então aqui está um link para a documentação (tabela em no topo da página): Documentação do Node A coisa mais importante a lembrar:
  1. SOMENTE os elementos possuem atributos.
  2. Os elementos NÃO têm significado.
  3. O nome do nó do elemento é igual ao nome da tag e o nome do nó do atributo é igual ao nome do atributo.
<h2>DOM (Document Object Model) - PRÁTICA</h2>Na parte prática analisaremos vários tipos de tarefas de busca de informações em XML. Também realizamos duas tarefas do artigo anterior para comparar a conveniência. Vamos começar, e seria bom começar pelas importações:
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;
Eu forneço importações para que você não confunda as classes :) Tarefa nº 1 - precisamos obter informações sobre todos os funcionários e enviá-las para o console a partir do seguinte arquivo 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 podemos ver, temos todas as informações armazenadas nos elementos dos funcionários. Para armazená-lo em algum lugar do nosso programa, vamos criar uma 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;
    }
}
Agora que temos uma descrição da estrutura de armazenamento de dados, precisamos de uma coleção que armazenará os funcionários. Iremos criá-lo no próprio código. Também precisamos criar um Documento baseado em nosso 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"));
    }
}
Assim que recebermos o documento, teremos poder ilimitado sobre toda a estrutura do arquivo XML. Podemos buscar qualquer elemento a qualquer momento, voltar para verificar qualquer dado e, em geral, uma abordagem mais flexível do que tínhamos no SAX. No contexto desta tarefa, precisamos apenas extrair todos os elementos dos funcionários e, em seguida, extrair todas as informações sobre eles. É bem simples:
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()));
    }
}
A descrição desta solução está certa na solução. É aconselhável, após visualizar o código, retornar à teoria e lê-lo novamente. Na verdade, tudo fica claro instintivamente. Leia os comentários com atenção e não deve haver dúvidas, e se houver, você pode escrever nos comentários, responderei, ou no link, ou apenas executar sua IDEIA e tentar brincar com o código você mesmo se você ainda não fez isso. Então, depois de executar o código, obtivemos a seguinte saída:
Информации о сотруднике: 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.
Como você pode ver, a tarefa foi concluída com sucesso! Vamos para a próxima tarefa :) Tarefa nº 2 - o nome de um elemento é inserido no console, sobre o qual você precisa exibir informações sobre todos os elementos dentro dele e seus atributos do seguinte arquivo 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>
Tudo é muito simples: devemos pegar o elemento pelo nome, que contamos, e depois passar por todos os nós filhos. Para fazer isso, você precisa iterar por todos os nós filhos de todos os nós filhos que são elementos. Solução para este 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());
        }
    }
}
A descrição completa da solução está nos comentários, mas gostaria de ilustrar um pouco graficamente a abordagem que utilizamos, usando um exemplo de uma imagem da teoria. Assumiremos que precisamos exibir informações sobre a tag html. Como você pode ver, precisamos ir de cima para baixo desde a raiz da árvore. Todas as linhas são nós. Na solução, iremos recursivamente desde o início do elemento desejado por todos os seus nós, e se um de seus nós for um elemento, também iteraremos por todos os nós deste elemento. Então, depois de executar o código, obtivemos a seguinte saída para o elemento raiz:
Элемент был найден!
Найден элемент: 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
O problema foi resolvido com sucesso! Tarefa nº 3 – a partir do seguinte arquivo XML, onde são salvas as informações sobre alunos, professores e funcionários, é necessário ler as informações e enviá-las para o 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>
A tarefa é bastante simples, mas interessante. Primeiro, precisamos criar 4 classes: funcionário, professor e aluno, bem como uma classe abstrata comum Humana para trazer o nome da variável de cada classe sob um denominador comum: Classe pai abstrata
public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
Estudante
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;
    }
}
Funcionário
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;
    }
}
Agora que nossas aulas estão prontas, só precisamos escrever o código para obter todos os elementos aluno, professor e membro, e depois obter seus atributos. Para armazenamento, utilizaremos uma coleção que armazenará objetos da classe pai comum a todos - Humano. E então, a solução para 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 {
        // Получение фабрики, чтобы после получить билдер 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;
            }
        }
    }
}
Observe que precisamos apenas do nome do elemento para obter todos esses elementos do documento. Isso simplifica muito o processo de localização das informações de que você precisa. Todas as informações sobre o código estão incluídas nos comentários. Não foi utilizado nada de novo que não estivesse presente nas tarefas anteriores. Saída 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 resolvido! Recomendações de quando usar DOM e quando usar SAX A diferença entre essas ferramentas está na funcionalidade e na velocidade. Se você precisa de funcionalidades mais flexíveis e pode desperdiçar o desempenho do programa, então sua escolha é o DOM, mas se o seu objetivo principal é reduzir os custos de memória, então o DOM não é a melhor escolha, pois lê todas as informações do arquivo XML e armazena. Portanto, o método de leitura sequencial SAX é mais barato. Resumidamente: se você precisa de desempenho - SAX, funcionalidade - DOM. <h2>Conclusão</h2>Cada programador possui suas próprias ferramentas e, dependendo da tarefa, é necessário utilizar determinadas ferramentas. Nos artigos sobre SAX e DOM, meu objetivo foi ensinar como extrair informações de arquivos XML e processá-las da maneira que você precisa. No entanto, mesmo que você tenha lido esses artigos, não pode afirmar que aprendeu como usar essas ferramentas. Você deve praticar, testar o código dos artigos, entender como funciona e tentar escrever algo sozinho. Afinal, o mais importante é a prática. O último artigo será publicado nos próximos dias e, aparentemente, após o término do concurso, e será dedicado ao JAXB. JAXB é uma ferramenta para salvar objetos em seu programa em formato XML. Só isso, espero que este artigo tenha sido útil, e boa sorte na sua programação :) Artigo anterior: [Competição] XML Basics para um Programador Java - Parte 3.1 de 3 - SAX
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION