JavaRush /مدونة جافا /Random-AR /أساسيات XML لمبرمج جافا. الجزء 3.2 من 3 - DOM
Ярослав
مستوى
Днепр

أساسيات XML لمبرمج جافا. الجزء 3.2 من 3 - DOM

نشرت في المجموعة
<h2>المقدمة</h2>مرحبًا بجميع قراء المقال، هذا الجزء مخصص لـ DOM. سيتم تخصيص الجزء التالي لـ JAXB، وبهذا ستكتمل دورة أساسيات XML. أولا سيكون هناك القليل من النظرية، وبعد ذلك فقط الممارسة. هيا بنا نبدأ. <h2>DOM (نموذج كائن المستند) - النظرية</h2>تم تصميم معالج DOM بحيث يقرأ كل XML مرة واحدة ويحفظه، مما يؤدي إلى إنشاء تسلسل هرمي على شكل شجرة يمكننا التحرك من خلاله بسهولة والوصول إلى العناصر التي نحتاجها. وبالتالي، يمكننا، بالنظر إلى رابط العنصر العلوي، الحصول على جميع الروابط إلى عناصره الداخلية. علاوة على ذلك، فإن العناصر الموجودة داخل العنصر هي أبناء هذا العنصر، وهو والدهم. بمجرد أن نقرأ ملف XML بالكامل في الذاكرة، سننتقل ببساطة عبر بنيته وننفذ الإجراءات التي نحتاجها. القليل عن جزء برمجة DOM في Java: يحتوي 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: ستُرجع دائمًا قيمة فارغة ما لم تستخدم طريقة setNamespaceAware(true) في DocumentFactory لتشغيل معالجة مساحة الاسم. الآن، تفصيل مهم: الأساليب مشتركة بين جميع العقد، ولكن في العقدة يمكن أن يكون لدينا عنصر وسمة. وهنا الأسئلة: ما القيمة التي يمكن أن يمتلكها العنصر؟ ما العقد المشتقة التي يمكن أن تمتلكها السمة؟ وغيرها غير متسقة. وكل شيء بسيط للغاية: ستعمل كل طريقة وفقًا لنوع العقدة . ويكفي استخدام المنطق بالطبع حتى لا نخلط. على سبيل المثال: ما هي السمات التي يمكن أن تمتلكها؟ ما المعنى الآخر الذي يحمله العنصر؟ ومع ذلك، حتى لا تجرب كل شيء بنفسك، يوجد في المستندات الرسمية جدول مفيد جدًا حول كيفية عمل كل طريقة اعتمادًا على نوع العقدة: تبين أن الجودة سيئة، لذا إليك رابط للوثائق (الجدول في أعلى الصفحة): توثيق العقدة أهم شيء يجب تذكره:
  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 لكي نجمع متغير الاسم من كل فئة تحت قاسم مشترك: فئة الأصل Abstract
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;
    }
}
الآن بعد أن أصبحت فصولنا جاهزة، نحتاج فقط إلى كتابة التعليمات البرمجية للحصول على جميع عناصر الطالب والأستاذ والعضو، ثم الحصول على سماتها. للتخزين، سوف نستخدم مجموعة تقوم بتخزين كائنات الفئة الأصلية المشتركة بين الجميع - الإنسان. وعليه فإن الحل لهذه المشكلة:
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 لمبرمج Java - الجزء 3.1 من 3 - SAX
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION