JavaRush /وبلاگ جاوا /Random-FA /مبانی XML برای برنامه نویس جاوا. قسمت 3.2 از 3 - DOM
Ярослав
مرحله
Днепр

مبانی XML برای برنامه نویس جاوا. قسمت 3.2 از 3 - DOM

در گروه منتشر شد
<h2>مقدمه</h2>سلام به همه خوانندگان مقاله، این قسمت به DOM اختصاص دارد. مورد بعدی به JAXB اختصاص خواهد یافت و با این کار، چرخه مبانی XML تکمیل خواهد شد. ابتدا کمی تئوری وجود خواهد داشت و سپس فقط عمل. بیا شروع کنیم. <h2>DOM (مدل شیء سند) - تئوری</h2> کنترل کننده DOM به گونه ای طراحی شده است که تمام XML را به یکباره می خواند و ذخیره می کند و سلسله مراتبی به شکل درختی ایجاد می کند که به راحتی می توانیم از طریق آن حرکت کنیم. و به عناصری که نیاز داریم دسترسی پیدا کنیم. بنابراین، با دادن پیوند به عنصر بالا، می‌توانیم همه پیوندها را به عناصر داخلی آن دریافت کنیم. علاوه بر این، عناصری که در داخل عنصر هستند فرزندان این عنصر هستند و آن والد آنهاست. هنگامی که تمام XML را در حافظه خواندیم، به سادگی در ساختار آن سفر کرده و اقدامات مورد نیاز خود را انجام خواهیم داد. کمی در مورد بخش برنامه نویسی DOM در جاوا: DOM دارای رابط های بسیاری است که برای توصیف داده های مختلف ایجاد شده اند. همه این رابط ها یک رابط مشترک را به ارث می برند - Node. زیرا در واقع رایج ترین نوع داده در DOM Node است که می تواند هر چیزی باشد. هر گره دارای روش های مفید زیر برای بازیابی اطلاعات است:
  1. getNodeName- نام میزبان را دریافت کنید.
  2. getNodeValue- مقدار گره را دریافت کنید.
  3. getNodeType- نوع گره را دریافت کنید.
  4. getParentNode- گرهی را که گره داده شده در آن قرار دارد را دریافت کنید.
  5. getChildNodes- دریافت تمام گره های مشتق شده (گره هایی که در داخل یک گره مشخص هستند).
  6. getAttributes- تمام ویژگی های گره را دریافت کنید.
  7. getOwnerDocument- سند این گره را دریافت کنید.
  8. getFirstChild/getLastChild- اولین/آخرین گره مشتق شده را دریافت کنید.
  9. getLocalName- هنگام پردازش فضاهای نام برای دریافت نام بدون پیشوند مفید است.
  10. getTextContent- تمام متن را در یک عنصر و تمام عناصر موجود در یک عنصر معین، از جمله خطوط شکسته و فاصله ها را برمی گرداند.
نکته در مورد روش 9: همیشه null برمی‌گرداند مگر اینکه از متد setNamespaceAware(true) در DocumentFactory برای راه‌اندازی پردازش فضای نام استفاده کرده باشید. حال، یک جزئیات مهم: متدها برای همه Node مشترک هستند، اما در Node می توانیم هم یک عنصر و هم یک ویژگی داشته باشیم. و در اینجا سؤالاتی مطرح می شود: یک عنصر چه ارزشی می تواند داشته باشد؟ یک ویژگی چه گره های مشتق شده ای می تواند داشته باشد؟ و دیگران سازگار نیستند. و همه چیز بسیار ساده است: هر روش بسته به نوع Node کار خواهد کرد . فقط کافی است از منطق استفاده کنید البته تا دچار سردرگمی نشوید. به عنوان مثال: صفات قادر به داشتن چه صفاتی هستند؟ این عنصر چه معنای دیگری دارد؟ با این حال، برای اینکه همه چیز را خودتان امتحان نکنید، در اسناد رسمی یک جدول بسیار مفید در مورد نحوه عملکرد هر روش بسته به نوع Node وجود دارد: کیفیت بد بود، بنابراین در اینجا پیوندی به مستندات وجود دارد (جدول در بالای صفحه): مستندات گره مهمترین چیزی که باید به خاطر بسپارید:
  1. فقط عناصر دارای ویژگی هستند.
  2. عناصر هیچ معنایی ندارند.
  3. نام گره عنصر با نام تگ و نام گره خصیصه با نام ویژگی یکی است.
<h2>DOM (مدل شیء سند) - تمرین</h2>در بخش عملی، انواع مختلفی از وظایف در جستجوی اطلاعات در XML را تجزیه و تحلیل خواهیم کرد. ما همچنین دو وظیفه از مقاله قبلی برای مقایسه راحتی انتخاب کردیم. بیایید شروع کنیم، و خوب است که با واردات شروع کنیم:
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;
من واردات ارائه می کنم تا کلاس ها را با هم اشتباه نگیرید :) وظیفه شماره 1 - باید اطلاعات مربوط به همه کارمندان را بدست آوریم و از فایل 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>
همانطور که می بینیم، ما تمام اطلاعات را در عناصر کارمند ذخیره می کنیم. برای اینکه آن را در جایی از برنامه خود ذخیره کنیم، بیایید یک کلاس ایجاد کنیم 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;
    }
}
اکنون که توضیحی در مورد ساختار ذخیره داده ها داریم، به مجموعه ای نیاز داریم که کارکنان را ذخیره کند. ما آن را در خود کد ایجاد خواهیم کرد. ما همچنین باید یک سند بر اساس 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"));
    }
}
پس از دریافت سند، قدرت نامحدودی بر کل ساختار فایل XML داریم. ما می‌توانیم هر عنصری را در هر زمان واکشی کنیم، برای بررسی هر داده به عقب برگردیم و به طور کلی، رویکردی انعطاف‌پذیرتر از آنچه در SAX داشتیم. در چارچوب این کار، ما فقط باید تمام عناصر کارمند را استخراج کنیم و سپس تمام اطلاعات مربوط به آنها را استخراج کنیم. کاملا ساده است:
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()));
    }
}
شرح این راه حل درست در راه حل است. توصیه می شود پس از مشاهده کد، به تئوری برگردید و دوباره آن را بخوانید. در واقع همه چیز به طور غریزی مشخص است. کامنت ها رو با دقت بخون و نباید سوالی باشه و اگه هست میتونی تو کامنت ها بنویسی جواب میدم یا تو لینک یا فقط IDEAتو اجرا کن و سعی کن خودت با کد بازی کنی شما هنوز این کار را نکرده اید بنابراین پس از اجرای کد خروجی زیر را دریافت کردیم:
Информации о сотруднике: 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.
همانطور که می بینید، کار با موفقیت انجام شد! بیایید به کار بعدی برویم :) وظیفه شماره 2 - نام یک عنصر از کنسول وارد شده است که در مورد آن باید اطلاعات مربوط به تمام عناصر داخل آن و ویژگی های آنها را از فایل 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>
همه چیز بسیار ساده است: ما باید عنصر را با نام آن، که می شماریم، دریافت کنیم، و سپس از تمام گره های فرزند عبور کنیم. برای انجام این کار، باید از طریق تمام گره های فرزند از تمام گره های فرزند که عناصر هستند، تکرار کنید. راه حل این مشکل:
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());
        }
    }
}
توضیحات کامل راه حل در نظرات است، اما من می خواهم با استفاده از مثالی از تصویری از تئوری، رویکردی را که استفاده کردیم، کمی به صورت گرافیکی نشان دهم. فرض می کنیم که باید اطلاعات مربوط به تگ html را نمایش دهیم. همانطور که می بینید، باید از ریشه درخت از بالا به پایین برویم. همه خطوط گره هستند. در راه حل به صورت بازگشتی از ابتدای عنصر مورد نظر در تمام گره های آن می گذریم و اگر یکی از گره های آن عنصر باشد، در تمام گره های این عنصر نیز تکرار می کنیم. بنابراین پس از اجرای کد خروجی زیر را برای عنصر ریشه دریافت کردیم:
Элемент был найден!
Найден элемент: 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
مشکل با موفقیت حل شد! وظیفه شماره 3 – از فایل XML زیر که اطلاعات دانشجویان، اساتید و کارمندان در آن ذخیره شده است، باید اطلاعات را بخوانید و به کنسول خروجی دهید:
<?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>
کار بسیار ساده است، اما جالب است. ابتدا باید 4 کلاس ایجاد کنیم: کارمند، استاد و دانشجو، و همچنین یک کلاس انتزاعی مشترک Human تا متغیر نام هر کلاس را تحت یک مخرج مشترک قرار دهیم: کلاس والد انتزاعی.
public abstract class Human {
    private String name;

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

    public String getName() {
        return name;
    }
}
دانشجو
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;
    }
}
استاد
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;
    }
}
کارمند
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;
    }
}
اکنون که کلاس های ما آماده است، فقط باید کد بنویسیم تا تمام عناصر دانشجو، استاد و عضو را دریافت کنیم و سپس ویژگی های آنها را دریافت کنیم. برای ذخیره سازی، ما از مجموعه ای استفاده خواهیم کرد که اشیاء کلاس والد مشترک برای همه - Human را ذخیره می کند. و بنابراین، راه حل این مشکل:
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;
            }
        }
    }
}
توجه داشته باشید که برای دریافت تمام این عناصر از سند فقط به نام عنصر نیاز داریم. این روند یافتن اطلاعات مورد نیاز شما را بسیار ساده می کند. تمام اطلاعات مربوط به کد در نظرات درج شده است. هیچ چیز جدیدی که در کارهای قبلی وجود نداشته باشد استفاده نشده است. خروجی کد:
Профессор 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
مشکل حل شد! توصیه هایی برای استفاده از DOM و زمان استفاده از SAX تفاوت این ابزارها در عملکرد و سرعت است. اگر به عملکرد انعطاف‌پذیرتر نیاز دارید و می‌توانید عملکرد برنامه را هدر دهید، انتخاب شما DOM است، اما اگر هدف اصلی شما کاهش هزینه‌های حافظه است، DOM بهترین انتخاب نیست، زیرا تمام اطلاعات فایل XML را می‌خواند و آن را ذخیره می کند. بنابراین، روش خواندن متوالی SAX هزینه کمتری دارد. به طور خلاصه: اگر به عملکرد نیاز دارید - SAX، عملکرد - DOM. <h2>نتیجه گیری</h2>هر برنامه نویس ابزارهای خاص خود را دارد و بسته به کار، باید از ابزارهای خاصی استفاده کنید. در مقاله‌هایی درباره SAX و DOM، هدف من این بود که به شما یاد بدهم چگونه اطلاعات را از فایل‌های XML استخراج کنید و آن‌ها را همانطور که نیاز دارید پردازش کنید. با این حال، حتی اگر این مقالات را خوانده باشید، نمی توانید ادعا کنید که نحوه استفاده از این ابزارها را یاد گرفته اید. شما باید تمرین کنید، کد مقالات را آزمایش کنید، نحوه کار آن را بدانید و سعی کنید خودتان چیزی بنویسید. بالاخره مهمترین چیز تمرین است. آخرین مقاله در روزهای آینده و ظاهراً پس از پایان مسابقه منتشر خواهد شد و به JAXB اختصاص خواهد یافت. JAXB ابزاری برای ذخیره اشیاء در برنامه شما با فرمت XML است. این همه بود، امیدوارم این مقاله مفید بوده باشد و در برنامه نویسی شما موفق باشید :) مقاله قبلی: [مسابقه] مبانی XML برای برنامه نویس جاوا - قسمت 3.1 از 3 - SAX
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION