JavaRush /Java Blog /Random-ID /Dasar-dasar XML untuk Pemrogram Java. Bagian 3.2 dari 3 -...
Ярослав
Level 40
Днепр

Dasar-dasar XML untuk Pemrogram Java. Bagian 3.2 dari 3 - DOM

Dipublikasikan di grup Random-ID
<h2>Pendahuluan</h2>Halo untuk semua pembaca artikel, bagian ini didedikasikan untuk DOM. Yang berikutnya akan dikhususkan untuk JAXB dan, dengan ini, siklus dasar-dasar XML akan selesai. Pertama akan ada sedikit teori, dan kemudian hanya praktek. Mari kita mulai. <h2>DOM (Model Objek Dokumen) - TEORI</h2>Penanganan DOM dirancang sedemikian rupa sehingga ia membaca semua XML sekaligus dan menyimpannya, menciptakan hierarki dalam bentuk pohon yang dapat kita gunakan untuk berpindah dengan mudah dan mengakses elemen yang kita perlukan. Jadi, dengan diberi link ke elemen teratas, kita bisa mendapatkan semua link ke elemen dalamnya. Selain itu, elemen-elemen yang ada di dalam elemen tersebut adalah anak-anak dari elemen tersebut, dan merupakan induknya. Setelah kita membaca semua XML ke dalam memori, kita cukup menelusuri strukturnya dan melakukan tindakan yang kita perlukan. Sedikit tentang bagian pemrograman DOM di Java: DOM memiliki banyak antarmuka yang dibuat untuk mendeskripsikan berbagai data. Semua antarmuka ini mewarisi satu antarmuka umum - Node.js. Sebab sebenarnya tipe data yang paling umum di DOM adalah Node, bisa apa saja. Setiap Node memiliki metode berguna berikut untuk mengambil informasi:
  1. getNodeName– dapatkan nama host.
  2. getNodeValue– dapatkan nilai simpul.
  3. getNodeType– dapatkan tipe node.
  4. getParentNode– dapatkan node di mana node tersebut berada.
  5. getChildNodes– dapatkan semua node turunan (node ​​yang berada di dalam node tertentu).
  6. getAttributes– dapatkan semua atribut node.
  7. getOwnerDocument– dapatkan dokumen node ini.
  8. getFirstChild/getLastChild– dapatkan node turunan pertama/terakhir.
  9. getLocalName– berguna saat memproses namespace untuk mendapatkan nama tanpa awalan.
  10. getTextContent– mengembalikan semua teks dalam suatu elemen dan semua elemen dalam elemen tertentu, termasuk jeda baris dan spasi.
Catatan tentang metode 9: metode ini akan selalu mengembalikan null kecuali Anda telah menggunakan metode setNamespaceAware(true) di DocumentFactory untuk memicu pemrosesan namespace. Sekarang, detail penting: metode ini umum untuk semua Node, tetapi di Node kita dapat memiliki elemen dan atribut. Dan inilah pertanyaannya: nilai apa yang dimiliki suatu elemen? Node turunan apa yang dapat dimiliki suatu atribut? Dan yang lainnya tidak konsisten. Dan semuanya cukup sederhana: setiap metode akan bekerja tergantung pada tipe Node . Tentu saja cukup menggunakan logika agar tidak bingung. Misalnya: atribut apa yang dapat dimiliki oleh atribut? Apa arti lain yang dimiliki elemen tersebut? Namun, agar tidak mencoba semuanya sendiri, di dokumen resmi terdapat tabel yang sangat berguna tentang cara kerja setiap metode tergantung pada jenis Node: Kualitasnya ternyata buruk, jadi berikut ini tautan ke dokumentasinya (tabel di bagian atas halaman): Dokumentasi Node Hal yang paling penting untuk diingat:
  1. HANYA elemen yang memiliki atribut.
  2. Elemen TIDAK memiliki arti.
  3. Nama node elemen sama dengan nama tag, dan nama node atribut sama dengan nama atribut.
<h2>DOM (Document Object Model) - PRAKTEK</h2>Pada bagian praktikum, kita akan menganalisis berbagai jenis tugas pencarian informasi dalam XML. Kami juga mengambil dua tugas dari artikel sebelumnya untuk membandingkan kenyamanan. Mari kita mulai, dan alangkah baiknya jika memulai dengan impor:

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;
Saya menyediakan impor agar Anda tidak membingungkan kelas :) Tugas No. 1 - kita perlu mendapatkan informasi tentang semua karyawan dan menampilkannya ke konsol dari file XML berikut:

<?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>
Seperti yang bisa kita lihat, kita memiliki semua informasi yang tersimpan di elemen karyawan. Untuk menyimpannya di suatu tempat di program kita, mari buat kelas 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;
    }
}
Sekarang kita memiliki gambaran tentang struktur penyimpanan data, kita memerlukan koleksi yang akan menyimpan karyawan. Kami akan membuatnya dalam kode itu sendiri. Kita juga perlu membuat Dokumen berdasarkan XML kita:

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")); 
    }
}
Setelah kami menerima dokumen tersebut, kami memiliki kekuasaan tak terbatas atas seluruh struktur file XML. Kami dapat mengambil elemen apa pun kapan saja, kembali untuk memeriksa data apa pun, dan secara umum, pendekatan yang lebih fleksibel daripada yang kami lakukan di SAX. Dalam konteks tugas ini, kita hanya perlu mengekstrak semua elemen karyawan, lalu mengekstrak semua informasi tentangnya. Ini cukup sederhana:

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()));
    }
}
Deskripsi solusi ini tepat dalam solusinya. Dianjurkan, setelah melihat kodenya, untuk kembali ke teori dan membacanya lagi. Faktanya, semuanya jelas secara naluriah. Bacalah komentar dengan seksama dan tidak boleh ada pertanyaan, dan jika ada, Anda dapat menulis di komentar, saya akan menjawab, atau di tautan, atau jalankan saja IDEA Anda dan coba mainkan sendiri kodenya jika kamu belum melakukannya. Jadi setelah menjalankan kode kami mendapatkan output berikut:

Информации о сотруднике: 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.
Seperti yang Anda lihat, tugas berhasil diselesaikan! Mari kita lanjutkan ke tugas berikutnya :) Tugas No. 2 - nama elemen dimasukkan dari konsol, yang tentangnya Anda perlu menampilkan informasi tentang semua elemen di dalamnya dan atributnya dari file XML berikut:

<?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>
Semuanya cukup sederhana: kita harus mendapatkan elemen berdasarkan namanya, yang kita hitung, dan kemudian menelusuri semua node anak. Untuk melakukan ini, Anda perlu melakukan iterasi melalui semua node anak dari semua node anak yang merupakan elemen. Solusi untuk masalah ini:

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());
        }
    }
}
Deskripsi keseluruhan solusi ada di komentar, tetapi saya ingin mengilustrasikan sedikit secara grafis pendekatan yang kami gunakan, menggunakan contoh dari gambaran teori. Kami berasumsi bahwa kami perlu menampilkan informasi tentang tag html. Seperti yang Anda lihat, kita perlu bergerak dari atas ke bawah dari akar pohon. Semua garis adalah simpul. Dalam solusinya, kita akan menelusuri secara rekursif dari awal elemen yang diinginkan melalui semua node-nya, dan jika salah satu node-nya adalah elemen, maka kita juga melakukan iterasi melalui semua node elemen ini. Jadi setelah menjalankan kode kami mendapatkan output berikut untuk elemen 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
Masalahnya telah berhasil diselesaikan! Tugas No.3 – dari file XML berikut, tempat informasi tentang mahasiswa, dosen, dan karyawan disimpan, Anda perlu membaca informasi tersebut dan menampilkannya ke konsol:

<?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>
Tugasnya cukup sederhana namun menarik. Pertama, kita perlu membuat 4 kelas: karyawan, profesor dan mahasiswa, serta kelas abstrak umum Manusia untuk membawa variabel nama dari setiap kelas di bawah penyebut yang sama: Kelas induk abstrak

public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
Murid

public class Student extends Human {
    private String course, specialization;

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

    public String getCourse() {
        return course;
    }

    public String getSpecialization() {
        return specialization;
    }

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

public class Professor extends Human {
    private String experience, discipline;

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

    public String getExperience() {
        return experience;
    }

    public String getDiscipline() {
        return discipline;
    }

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

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;
    }
}
Sekarang kelas kita sudah siap, kita hanya perlu menulis kode untuk mendapatkan semua elemen mahasiswa, profesor, dan anggota, lalu mendapatkan atributnya. Untuk penyimpanan, kami akan menggunakan koleksi yang akan menyimpan objek dari kelas induk yang umum untuk semua - Manusia. Jadi, solusi untuk masalah ini:

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;
            }
        }
    }
}
Perhatikan bahwa kita hanya memerlukan nama elemen untuk mendapatkan semua elemen ini dari dokumen. Ini sangat menyederhanakan proses menemukan informasi yang Anda butuhkan. Semua informasi tentang kode disertakan dalam komentar. Tidak ada hal baru yang digunakan yang tidak ada pada tugas sebelumnya. Keluaran kode:

Профессор 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
Masalah terpecahkan! Rekomendasi kapan menggunakan DOM dan kapan menggunakan SAX Perbedaan antara alat-alat ini terletak pada fungsionalitas dan kecepatan. Jika Anda memerlukan fungsionalitas yang lebih fleksibel dan mampu membuang kinerja program, maka pilihan Anda adalah DOM, tetapi jika tujuan utama Anda adalah mengurangi biaya memori, maka DOM bukanlah pilihan terbaik, karena DOM membaca semua informasi dari file XML dan menyimpannya. Oleh karena itu, metode pembacaan sekuensial SAX lebih murah. Secara singkat: jika Anda memerlukan kinerja - SAX, fungsionalitas - DOM. <h2>Kesimpulan</h2>Setiap programmer memiliki alatnya sendiri, dan, bergantung pada tugasnya, Anda perlu menggunakan alat tertentu. Dalam artikel tentang SAX dan DOM, tujuan saya adalah mengajari Anda cara mengekstrak informasi dari file XML dan memprosesnya sesuai kebutuhan. Namun, meskipun Anda telah membaca artikel ini, Anda tidak dapat mengklaim telah mempelajari cara menggunakan alat ini. Anda harus berlatih, menguji kode dari artikel, memahami cara kerjanya, dan mencoba menulis sesuatu sendiri. Bagaimanapun, yang terpenting adalah latihan. Artikel terakhir akan diterbitkan dalam beberapa hari mendatang dan, tampaknya, setelah kompetisi berakhir, dan akan dikhususkan untuk JAXB. JAXB adalah alat untuk menyimpan objek dalam program Anda dalam format XML. Itu saja, semoga artikel ini bermanfaat, dan semoga sukses dalam pemrograman Anda :) Artikel sebelumnya: [Kompetisi] Dasar-Dasar XML untuk Pemrogram Java - Bagian 3.1 dari 3 - SAX
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION