JavaRush /Java блогу /Random-KY /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деги эң кеңири таралган маалымат түрү бул Node, ал каалаган нерсе болушу мүмкүн. Ар бир түйүндө маалыматты алуу үчүн төмөнкү пайдалуу ыкмалары бар:
  1. getNodeName– хосттун атын алуу.
  2. getNodeValue– түйүн маанисин алуу.
  3. getNodeType– түйүн түрүн алуу.
  4. getParentNode– берилген түйүн жайгашкан түйүндү алуу.
  5. getChildNodes– бардык туунду түйүндөрдү алуу (берилген түйүндүн ичиндеги түйүндөр).
  6. getAttributes– бардык түйүн атрибуттарын алуу.
  7. getOwnerDocument– бул түйүндүн documentин алуу.
  8. getFirstChild/getLastChild– биринчи/акыркы алынган түйүндү алуу.
  9. getLocalName– префикси жок ат алуу үчүн аттар мейкиндигин иштетүүдө пайдалуу.
  10. getTextContent– элементтин ичиндеги бардык текстти жана берилген элементтин ичиндеги бардык элементтерди, анын ичинде сызыктарды жана боштуктарды кайтарат.
9-ыкма боюнча эскертүү: аттар мейкиндигин иштетүү үчүн DocumentFactory ичинде setNamespaceAware(true) ыкмасын колдонбосоңуз, ал ар дайым нөл болуп калат. Эми маанилүү детал: методдор бардык Түйүндөр үчүн жалпы, бирок Түйүндө биз элементке да, атрибутка да ээ боло алабыз. Бул жерде суроолор бар: элемент кандай мааниге ээ болушу мүмкүн? Атрибут кандай туунду түйүндөргө ээ болушу мүмкүн? Ал эми башкалар ырааттуу эмес. Жана баары абдан жөнөкөй: ар бир ыкма Node түрүнө жараша иштейт . Баш аламан болбош үчүн, албетте, логиканы колдонуу жетиштүү. Мисалы: кандай атрибуттарга ээ боло алат? Элементтин дагы кандай мааниси бар? Бирок, баарын өзүңүз сынап көрбөш үчүн, расмий documentтерде ар бир ыкма Түйүндүн түрүнө жараша кандай иштээри жөнүндө абдан пайдалуу table бар: Сапаты начар болуп чыкты, андыктан бул жерде documentацияга шилтеме (table: беттин үстү): Түйүндүн documentтери Эң негизгиси эстен чыгарбоо керек:
  1. ГАНА элементтердин атрибуттары бар.
  2. Элементтер эч кандай мааниге ээ эмес.
  3. Элемент түйүнүнүн аталышы тегтин аты менен, ал эми атрибут түйүнүнүн аты атрибуттун аталышы менен бирдей.
<h2>DOM (Document Object Model) - ПРАКТИКА</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.
Көрүнүп тургандай, тапшырма ийгorктүү аткарылды! Кийинки тапшырмага өтөбүз :) Тапшырма №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
Маселе ийгorктүү чечилди! Тапшырма №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 жазып, анан алардын атрибуттарын алышыбыз керек. Сактоо үчүн биз бардыгына бирдей болгон ата-эне классынын an objectтерин сактай турган коллекцияны колдонобуз - Адам. Ошентип, бул маселени чечүү:
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 форматында программаңыздагы an objectтерди сактоо куралы. Болгону, бул макала пайдалуу болду деп ишенем жана программалооңузга ийгorк :) Мурунку макала: [Конкурс] Java программист үчүн XML негиздери - 3-бөлүктүн 3.1 - SAX
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION