JavaRush /Java блогы /Random-KK /Java бағдарламашысына арналған XML негіздері. 3/3.2 бөлім...
Ярослав
Деңгей
Днепр

Java бағдарламашысына арналған XML негіздері. 3/3.2 бөлімі - DOM

Топта жарияланған
<h2>Кіріспе</h2>Мақаланың барлық оқырмандарына сәлем! Бұл бөлім DOM-ға арналған. Келесісі JAXB-ге арналады және осымен XML негіздерінің циклі аяқталады. Алдымен кішкене теория, содан кейін тек практика болады. Бастайық. <h2>DOM (құжат нысанының үлгісі) - ТЕОРИЯ</h2>DOM өңдеушісі барлық XML-ді бірден оқитын және оны сақтайтындай етіп жасалған, осылайша ағаш түрінде иерархия жасайды, ол арқылы біз оңай жылжытамыз. және бізге қажетті элементтерге қол жеткізу. Осылайша, біз жоғарғы элементке сілтеме беріліп, оның ішкі элементтеріне барлық сілтемелерді ала аламыз. Сонымен қатар, элементтің ішіндегі элементтер осы элементтің еншілестері және ол олардың ата-анасы болып табылады. Барлық XML жадында оқып болғаннан кейін, біз жай ғана оның құрылымын аралап, қажетті әрекеттерді орындаймыз. Java-дағы DOM-тың бағдарламалау бөлігі туралы аздап: DOM-да әртүрлі деректерді сипаттау үшін жасалған көптеген интерфейстер бар. Барлық осы интерфейстер бір ортақ интерфейсті мұралайды - Node. Өйткені, шын мәнінде, DOM ішіндегі ең көп таралған деректер түрі - кез келген нәрсе болуы мүмкін Түйін. Әрбір түйінде ақпаратты алу үшін келесі пайдалы әдістер бар:
  1. getNodeName– хост атауын алыңыз.
  2. getNodeValue– түйін мәнін алу.
  3. getNodeType– түйін түрін алыңыз.
  4. getParentNode– берілген түйін орналасқан түйінді алу.
  5. getChildNodes– барлық туынды түйіндерді алу (берілген түйіннің ішіндегі түйіндер).
  6. getAttributes– барлық түйін атрибуттарын алу.
  7. getOwnerDocument– осы түйіннің құжатын алыңыз.
  8. getFirstChild/getLastChild– бірінші/соңғы алынған түйінді алыңыз.
  9. getLocalName– префикссіз атау алу үшін аттар кеңістігін өңдеу кезінде пайдалы.
  10. getTextContent– элемент ішіндегі барлық мәтінді және берілген элемент ішіндегі барлық элементтерді, соның ішінде жол үзілімдері мен бос орындарды қайтарады.
9-әдіс бойынша ескертпе: аттар кеңістігін өңдеуді іске қосу үшін DocumentFactory ішіндегі setNamespaceAware(true) әдісін пайдаланбасаңыз, ол әрқашан нөлді қайтарады. Енді маңызды деталь: әдістер барлық Түйіндер үшін ортақ, бірақ Түйінде бізде элемент пен атрибут болуы мүмкін. Міне, сұрақтар: элемент қандай мәнге ие болуы мүмкін? Атрибуттың қандай туынды түйіндері болуы мүмкін? Ал басқалары біркелкі емес. Және бәрі өте қарапайым: әрбір әдіс 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;
    }
}
Енді бізде деректерді сақтауға арналған құрылымның сипаттамасы бар, бізге қызметкерлерді сақтайтын жинақ қажет. Біз оны codeтың өзінде жасаймыз. Біз сондай-ақ 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-ды іске қосыңыз және codeпен өзіңіз ойнап көріңіз, егер сен әлі олай істеген жоқсың. Осылайша, codeты іске қосқаннан кейін біз келесі нәтижені алдық:
Информации о сотруднике: 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 тегі туралы ақпаратты көрсетуіміз керек деп есептейміз. Көріп отырғаныңыздай, ағаштың түбінен жоғарыдан төменге қарай өту керек. Барлық жолдар түйіндер. Шешімде біз қалаған элементтің басынан оның барлық түйіндері арқылы рекурсивті түрде өтеміз және оның түйіндерінің бірі элемент болса, онда біз де осы элементтің барлық түйіндері арқылы қайталаймыз. Осылайша, codeты іске қосқаннан кейін біз түбірлік элемент үшін келесі нәтижені алдық:
Элемент был найден!
Найден элемент: 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 сыныпты құруымыз керек: қызметкер, профессор және студент, сондай-ақ жалпы дерексіз класс Адам әр сыныптан айнымалының атауын ортақ бөлгішке келтіру үшін: Абстрактілі ата-аналар класы
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;
    }
}
Енді біздің сабақтарымыз дайын, бізге студент, профессор және мүшенің барлық элементтерін алу үшін code жазу керек, содан кейін олардың атрибуттарын алу керек. Сақтау үшін біз барлығына ортақ ата-ана класының нысандарын сақтайтын топтаманы қолданамыз - Адам. Сонымен, бұл мәселенің шешімі:
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 файлдарынан ақпаратты алуды және оларды қажет етіп өңдеуді үйрету болды. Дегенмен, сіз осы мақалаларды оқыған болсаңыз да, сіз бұл құралдарды пайдалануды үйрендім деп айта алмайсыз. Сіз тәжірибе жасап, мақалалардағы codeты тексеріп, оның қалай жұмыс істейтінін түсініп, өзіңіз бірдеңе жазуға тырысуыңыз керек. Өйткені, ең бастысы - тәжірибе. Соңғы мақала жақын күндері және, шамасы, байқау аяқталғаннан кейін жарияланады және JAXB-ге арналады. JAXB – XML пішімінде бағдарламаңыздағы нысандарды сақтауға арналған құрал. Міне, осы мақала пайдалы болды деп үміттенемін және бағдарламалауда сәттілік :) Алдыңғы мақала: [Байқау] Java бағдарламашысына арналған XML негіздері - 3-бөлім 3.1 - SAX
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION