JavaRush /جاوا بلاگ /Random-UR /XML بنیادی باتیں جاوا پروگرامر کے لیے - حصہ 3.1 از 3 - SA...
Ярослав
سطح
Днепр

XML بنیادی باتیں جاوا پروگرامر کے لیے - حصہ 3.1 از 3 - SAX

گروپ میں شائع ہوا۔
تعارف میرے آخری مضمون کے تمام قارئین کو ہیلو، اور میں آپ کو مبارکباد دینا چاہتا ہوں: XML کے بارے میں پیچیدہ چیزیں ہمارے پیچھے ہیں۔ یہ مضمون جاوا میں کوڈ پر مشتمل ہوگا۔ ایک چھوٹی سی تھیوری ہوگی، اور پھر پریکٹس ہوگی۔ اس حقیقت کی وجہ سے کہ SAX پر مواد کے ایک ٹکڑے نے Word میں 10 صفحات بھرے ہیں، میں نے محسوس کیا کہ میں حدود میں فٹ نہیں ہو سکتا۔ لہذا، آرٹیکل 3 کو 3 الگ الگ مضامین میں تقسیم کیا جائے گا، چاہے یہ کتنا ہی عجیب کیوں نہ ہو۔ سب کچھ اس ترتیب میں ہوگا: SAX -> DOM -> JAXB۔ یہ مضمون صرف SAX پر توجہ مرکوز کرے گا۔ PS کورس میں کہیں ایک کام تھا جہاں HTML فائل میں تمام اندرونی عناصر کو ظاہر کرنا ضروری تھا۔ اس مضمون کے بعد، آپ روایتی اور پیچیدہ پروسیسنگ الگورتھم کے ساتھ لائن بہ لائن پڑھے بغیر ایسا کر سکیں گے BufferedReader، اور اسی طرح کا حل بھی آخری عملی مثال میں دیا جائے گا۔ آئیے شروع کرتے ہیں :) SAX (Simple API for XML) - تھیوری SAX ہینڈلر کو اس طرح سے ڈیزائن کیا گیا ہے کہ یہ آسانی سے XML فائلوں کو ترتیب وار پڑھتا ہے اور مختلف واقعات پر ردعمل ظاہر کرتا ہے، جس کے بعد یہ معلومات کو ایک خصوصی ایونٹ ہینڈلر تک پہنچاتا ہے۔ اس میں بہت سے واقعات ہیں، لیکن سب سے زیادہ بار بار اور مفید مندرجہ ذیل ہیں:
  1. startDocument- دستاویز کا آغاز
  2. endDocument- دستاویز کا اختتام
  3. startElement- ایک عنصر کھولنا
  4. endElement- ایک عنصر کو بند کرنا
  5. characters- عناصر کے اندر متن کی معلومات۔
تمام واقعات کو ایک ایونٹ ہینڈلر میں پروسیس کیا جاتا ہے ، جس کو بنانا اور طریقوں کو اوور رائڈ کرنا ضروری ہے ۔ فوائد: ڈیٹا پڑھنے کے "براہ راست" طریقہ کی وجہ سے اعلی کارکردگی، میموری کی کم لاگت۔ نقصانات: محدود فعالیت، جس کا مطلب ہے کہ غیر خطی مسائل میں ہمیں اسے بہتر کرنا پڑے گا۔ SAX (XML کے لیے سادہ API) - فوری طور پر درآمدات کی ایک فہرست پر عمل کریں تاکہ آپ کسی بھی چیز کو تلاش اور الجھا نہ دیں:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
اب، پہلے، ہمیں ایک SAXParser بنانے کی ضرورت ہے:
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Creation фабрики и образца parserа
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }
}
جیسا کہ آپ دیکھ سکتے ہیں، آپ کو سب سے پہلے ایک فیکٹری بنانے کی ضرورت ہے، اور پھر فیکٹری میں ہی پارسر بنانا ہوگا۔ اب جب کہ ہمارے پاس خود تجزیہ کار ہے، ہمیں اس کے واقعات کے لیے ایک ہینڈلر کی ضرورت ہے۔ اس کے لیے ہمیں اپنی سہولت کے لیے الگ کلاس کی ضرورت ہے۔
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            // Тут будет логика реакции на начало documentа
        }

        @Override
        public void endDocument() throws SAXException {
            // Тут будет логика реакции на конец documentа
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            // Тут будет логика реакции на начало element
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            // Тут будет логика реакции на конец element
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            // Тут будет логика реакции на текст между elementми
        }

        @Override
        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
            // Тут будет логика реакции на пустое пространство внутри элементов (пробелы, переносы строчек и так далее).
        }
    }
}
Мы создали класс со всеми нужными нам методами для обработки событий, которые были перечислены в теории. Еще немного дополнительной теории: Немного про characters: если в элементе будет текст, например, «hello», то, теоретически, метод способен вызваться 5 раз подряд на каждый отдельный символ, однако это не страшно, так How все равно все будет работать. О методах startElement и endElement: uri — это пространство, в котором находится элемент, localName — это Name element без префикса, qName — это Name element с префиксом (если он есть, иначе просто Name element). uri и localName всегда пустые, если мы не подключor в фабрике обработку пространств. Это делается методом фабрики setNamespaceAware(true). Тогда мы сможем получать пространство (uri) и элементы с префиксами перед ними (localName). Задача №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>
Наша цель: достать всю информацию про всех сотрудников из данного file. Для начала, нам нужно создать класс 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;
    }
}
А в нашем основном классе SAXExample нам нужен список со всеми сотрудниками:
private static ArrayList<Employee> employees = new ArrayList<>();
Теперь давайте внимательно смотреть, где нужная нам информация находится в XML файле. И, How мы можем видеть, вся нужная нам информация — это атрибуты элементов employee. А так, How startElement у нас обладает таким полезным параметром, How attributes, то у нас довольно простая задача. Для начала, давайте уберем ненужные методы, чтобы не захламлять наш code. Нам нужен только метод startElement. А в самом методе мы должны собрать информацию с атрибутов тега employee. Внимание:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equals("employee")) {
                String name = attributes.getValue("name");
                String job = attributes.getValue("job");
                employees.add(new Employee(name, job));
            }
        }
    }
}
Логика простая: если Name element — employee, мы просто будем получать информацию про его атрибуты. В attributes есть полезный метод, где, зная название атрибута, можно получить его meaning. Именно его мы и использовали. Теперь, когда мы создали обрабатывание события на начало element, нам нужно запарсить наш XML файл. Для этого достаточно сделать так:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        XMLHandler handler = new XMLHandler();
        parser.parse(new File("resource/xml_file1.xml"), handler);

        for (Employee employee : employees)
            System.out.println(String.format("Name сотрудника: %s, его должность: %s", employee.getName(), employee.getJob()));
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equals("employee")) {
                String name = attributes.getValue("name");
                String job = attributes.getValue("job");
                employees.add(new Employee(name, job));
            }
        }
    }
}
В методе parse вы должны передать way to xml файлу и обработчик, который вы создали. И так, с помощью данного codeа мы достали информацию из этого 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>
А выходные данные мы получor такие:
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"?>
<company>
    <name>IT-Heaven</name>
    <offices>
        <office floor="1" room="1">
            <employees>
                <employee>
                    <name>Maksim</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Ivan</name>
                    <job>Junior Software Developer</job>
                </employee>
                <employee>
                    <name>Franklin</name>
                    <job>Junior Software Developer</job>
                </employee>
            </employees>
        </office>
        <office floor="1" room="2">
            <employees>
                <employee>
                    <name>Herald</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Adam</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Leroy</name>
                    <job>Junior Software Developer</job>
                </employee>
            </employees>
        </office>
    </offices>
</company>
Наша цель: достать всю информацию про всех сотрудников из данного file. Задача хорошо продемонстрирует, Howим образом плохо структурированный XML файл может приводить к усложнению написания codeа. Как вы видите, информация про Name и должность теперь хранится How текстовая информация внутри элементов name и job. Для считывания текста внутри элементов у нас есть метод characters. Для этого, нам нужно создать новый класс-обработчик с улучшенной логикой. Не забывайте, что обработчики – полноценные классы, способные хранить в себе логику любой сложности. Потому, сейчас мы будем тюнинговать наш обработчик. На самом деле, достаточно заметить, что у нас всегда name и job идут по очереди, и не важно, в Howом порядке, мы можем спокойно сохранить Name и профессию в отдельные переменные, и когда обе переменные сохранены – создать нашего сотрудника. Только вот вместе с началом element у нас нет параметра для текста внутри element. Нам нужно использовать методы для текста. Но How нам получить текстовую информацию внутри element, если это совершенно разные методы? Мое решение: нам достаточно запомнить Name последнего element, а в characters проверять, в Howом элементе мы считываем информацию. Так же нужно помнить, что <codee>characters считывает все символы внутри элементов, а это значит, что будут считываться все пробелы и даже переносы строчек. А они нам не нужны. Нам нужно игнорировать эти данные, так How они неправильные.</codee> Код:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        AdvancedXMLHandler handler = new AdvancedXMLHandler();
        parser.parse(new File("resource/xml_file2.xml"), handler);

        for (Employee employee : employees)
            System.out.println(String.format("Name сотрудника: %s, его должность: %s", employee.getName(), employee.getJob()));
    }

    private static class AdvancedXMLHandler extends DefaultHandler {
        private String name, job, lastElementName;

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            lastElementName = qName;
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String information = new String(ch, start, length);

            information = information.replace("\n", "").trim();

            if (!information.isEmpty()) {
                if (lastElementName.equals("name"))
                    name = information;
                if (lastElementName.equals("job"))
                    job = information;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if ( (name != null && !name.isEmpty()) && (job != null && !job.isEmpty()) ) {
                employees.add(new Employee(name, job));
                name = null;
                job = null;
            }
        }
    }
}
Как вы видите, из-за банального усложнения структуры XML file у нас значительно усложнился code. Однако, code не сложный. Описание: мы создали переменные для хранения данных про сотрудника (name, job), а так же переменную lastElementName, чтобы фиксировать, внутри Howого element мы находимся. После этого, в методе characters мы фильтруем информацию, и если там еще осталась информация, то, значит, это нужный нам текст, а далее мы определяем, Name это or профессия, используя lastElementName. В методе endElement мы проверяем, считана ли вся информация, и если считана, то мы создаем сотрудника и сбрасываем информацию. Выходные данные решения эквивалентны первому примеру:
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
Таким образом, данная задача была решена, но вы можете заметить то, что сложность выше. Потому можно сделать вывод, что хранить текстовую информацию в атрибутах чаще всего будет правильней, чем в отдельных elementх. И еще одна сладкая задача, которая будет частично решать задачу на JavaRush про вывод информации об элементе в HTML, только её надо будет немного подредактировать, тут мы будем просто перечислять все элементы внутри Howого-то element :) Задача №3 — дан элемент element, вывести имена и атрибуты всех внутренних элементов, если элемент не найден — вывести это. Для данной задачи мы будем использовать следующий 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>
Как вы видите, у нас тут есть три возможных сценария: root, mysql, oracle. Тогда программа будет выводить всю инфу о всех elementх внутри. Как же нам сделать такое? А достаточно просто: нам достаточно объявить логическую переменную isEntered, которая будет означать, внутри ли мы нужно нам element, и если внутри – считывать все данные из startElement. Код решения:
public class SAXExample {
    private static boolean isFound;

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        SearchingXMLHandler handler = new SearchingXMLHandler("root");
        parser.parse(new File("resource/xml_file3.xml"), handler);

        if (!isFound)
            System.out.println("Элемент не был найден.");
    }

    private static class SearchingXMLHandler extends DefaultHandler {
        private String element;
        private boolean isEntered;

        public SearchingXMLHandler(String element) {
            this.element = element;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (isEntered) {
                System.out.println(String.format("Найден элемент <%s>, его атрибуты:", qName));

                int length = attributes.getLength();
                for(int i = 0; i < length; i++)
                    System.out.println(String.format("Name атрибута: %s, его meaning: %s", attributes.getQName(i), attributes.getValue(i)));
            }

            if (qName.equals(element)) {
                isEntered = true;
                isFound = true;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equals(element))
                isEntered = false;
        }
    }
}
В данном codeе мы при входе в элемент, про который нам нужна информация, выставляем флажок isEntered в true, что значит, что мы внутри element. И How только мы оказались внутри element, мы просто каждый новый элемент в startElement обрабатываем, зная, что он точно внутренний элемент нашего element. Таким образом, мы выводим Name element и его название. Если же элемент не был найден в файле, то у нас есть переменная isFound, которая устанавливается тогда, когда элемент находится, и если она false, то будет выведено сообщение, что элемент не найден. И How вы видите, в примере в конструктор SearchingXMLHandler мы передали root элемент. Вывод для него:
Найден элемент <oracle>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Name атрибута: value, его meaning: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Name атрибута: value, его meaning: secretOracleUsername
Найден элемент <password>, его атрибуты:
Name атрибута: value, его meaning: 111
Найден элемент <mysql>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Name атрибута: value, его meaning: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Name атрибута: value, его meaning: secretMySQLUsername
Найден элемент <password>, его атрибуты:
Name атрибута: value, его meaning: 222
Таким образом, мы получor всю информацию про внутренние элементы и их атрибуты. Задача решена. <h2>Эпилог</h2>Вы ознакомorсь, что SAX довольно интересный инструмент и вполне эффективный, и его можно использовать по-разному, с разными целями и так далее, достаточно только посмотреть на задачу с правильной стороны, How это показано в задаче №2 и №3, где SAX не предоставлял прямых методов для решения задачи, но, благодаря нашей смекалке, у нас получилось придумать выход из ситуации. Следующая часть статьи будет целиком посвящена DOM. Надеюсь, что вам было интересно познакомиться с SAX. Поэкспериментируйте, попрактикуйтесь и вы поймете, что все довольно просто. А на этом все, удачи вам в программировании и ждите скоро часть про DOM. Успехов вам в обучении :) Предыдущая статья: [Конкурс] Основы XML для Java программиста - Часть 2 из 3 Следующая статья: [Конкурс] Основы XML для Java программиста - Часть 3.2 из 3 - DOM
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION