JavaRush /Java блогу /Random-KY /Java программист үчүн XML негиздери - 3-бөлүктүн 3.1 - SA...
Ярослав
Деңгээл
Днепр

Java программист үчүн XML негиздери - 3-бөлүктүн 3.1 - SAX

Группада жарыяланган
Киришүү Менин акыркы макаламдын бардык окурмандарына салам жана мен сизди куттуктагым келет: XML жөнүндө татаал нерселер артта калды. Бул макалада Java codeу болот. Бир аз теория, анан практика болот. SAX боюнча бир материал Word программасында 10 бетти толтургандыктан, мен чектерге бата албасымды түшүндүм. Демек, 3-берене канчалык кызыктай угулбасын, 3 өзүнчө беренеге бөлүнөт. Баары ушундай тартипте болот: SAX -> DOM -> JAXB. Бул макала SAX боюнча гана багытталган. PS Курстун бир жеринде HTML файлындагы бардык ички элементтерди көрсөтүү зарыл болгон тапшырма бар болчу. Бул макаладан кийин, сиз муну кадимки жана татаал иштетүү алгоритмдери менен сапка окубастан жасай аласыз BufferedReader, ошондой эле ушул сыяктуу чечим акыркы практикалык мисалда берилет. Баштайлы :) SAX (Simple API for XML) - ТЕОРИЯ SAX иштетүүчүсү XML файлдарын ырааттуулук менен окуп, ар кандай окуяларга реакция кылып, андан кийин маалыматты атайын окуяны иштеткичке өткөрүп бере тургандай иштелип чыккан. Анда бир нече окуялар бар, бирок эң көп жана пайдалуу болуп төмөнкүлөр саналат:
  1. startDocument— documentтин башталышы
  2. endDocument- documentтин аягы
  3. startElement- элементти ачуу
  4. endElement- элементти жабуу
  5. characters— элементтердин ичиндеги тексттик маалымат.
Бардык окуялар окуяны иштеткичте иштетилет , ал түзүлүшү керек жана методдор жокко чыгарылат . Артыкчылыктары: маалыматтарды окуунун "түз" ыкмасынан улам жогорку өндүрүмдүүлүк, эстутумдун баасы аз. Кемчorктери: чектелген функционалдык, бул сызыктуу эмес маселелерде биз аны тактоого туура келет дегенди билдирет. 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: эгерде элементте текст болсо, мисалы, “ салам ”, анда теориялык жактан ыкманы ар бир белги үчүн катары менен 5 жолу чакырса болот, бирок бул чоң маселе эмес, анткени баары иштей берет. жана ыкмалары жөнүндө :startElementendElementuri - бул элемент жайгашкан мейкиндик, localName- бул префикси жок элементтин аталышы, qName- бул префикси бар элементтин аталышы (эгерде бирөө болсо, болбосо жөн гана аты элементтин). uriжана localNameзаводдо мейкиндикти иштетүүнү иштете элек болсок, ар дайым бош. Бул заводдук ыкма менен жасалат 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>
Биздин максат: бул файлдан бардык кызматкерлер жөнүндө бардык маалыматтарды алуу. Биринчиден, биз классты түзүшүбүз керек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 файлында кайсы жерде экенин кылдаттык менен карап көрөлү. Жана, биз көрүп тургандай, бизге керектүү бардык маалымат элементтердин атрибуттары болуп саналат employee. Жана startElementбизде ушундай пайдалуу параметр бар болгондуктан attributes, бизде абдан жөнөкөй тапшырма бар. Биринчиден, codeубузду бузуп албаш үчүн керексиз ыкмаларды алып салалы. Бизге бир гана керек startElement. Ал эми методдун өзүндө биз кызматкердин тегинин атрибуттарынан маалымат чогултушубуз керек. Көңүл буруңуз:
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));
            }
        }
    }
}
Логика жөнөкөй: элементтин аты болсо employee, биз жөн гана анын атрибуттары жөнүндө маалымат алабыз. attributesАтрибуттун атын бorп, анын маанисин ала турган пайдалуу ыкма бар . Мына ошону колдонгонбуз. Эми элементтин башталышы үчүн окуяны иштеткичти түздүк, биз 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));
            }
        }
    }
}
Талдоо методунда сиз 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>
Жана биз төмөнкү натыйжаны алдык:
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>
Биздин максат: бул файлдан бардык кызматкерлер жөнүндө бардык маалыматтарды алуу. Бул көйгөй начар структураланган XML файлы code жазууну кантип кыйындата аларын жакшы көрсөтөт. Көрүнүп тургандай, аты жана орду жөнүндө маалымат азыр nameжана элементтеринин ичинде тексттик маалымат катары сакталат job. Элементтердин ичиндеги текстти окуу үчүн бизде символдор ыкмасы бар. Бул үчүн, биз логикасы жакшырган жаңы иштеткич классын түзүшүбүз керек. Иштетүүчүлөр ар кандай татаалдыктагы логиканы сактоого жөндөмдүү толук кандуу класстар экенин унутпаңыз. Ошондуктан, азыр биз процессорубузду тууралайбыз. nameЧынында, биз ар дайым кезектешип турганыбызды белгилей кетүү жетиштүү jobжана кандай тартипте болбосун, биз аты-жөнү менен кесипти өзүнчө өзгөрмөлөргө оңой сактай алабыз жана эки өзгөрмө тең сакталганда, биздин кызматкерди түзүңүз. Бул жерде гана элементтин башталышы менен бирге бизде элементтин ичиндеги текст үчүн параметр жок. Биз текст боюнча ыкмаларды колдонуу керек. Бирок бул такыр башка ыкмалар болсо, элементтин ичиндеги тексттик маалыматты кантип алабыз? Менин чечимим: биз жөн гана акыркы элементтин атын эстеп, charactersмаалыматты кайсы элементте окуп жатканыбызды текшеришибиз керек. Ошондой эле, <codee> символдору элементтердин ичиндеги бардык символдорду окуарын эстен чыгарбоо керек, бул бардык боштуктар жана ал тургай сызыктар да окулат дегенди билдирет. А бизге алардын кереги жок. Бул маалыматты этибарга алышыбыз керек, анткени ал туура эмес.</codee> Code:
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 файл структурасынын баналдык татаалдыгынан улам, биздин codeубуз кыйла татаалдашып кетти. Бирок, code татаал эмес. Сүрөттөмө: биз кызматкер ( name, job) жөнүндө маалыматтарды сактоо үчүн өзгөрмөлөрдү , ошондой эле lastElementNameкайсы элементтин ичинде экенибизди жазуу үчүн өзгөрмө түздүк. Андан кийин, ыкмада charactersбиз маалыматты чыпкалайбыз, эгер ал жерде дагы деле маалымат бар болсо, анда бул бизге керектүү текст экенин билдирет, андан кийин биз анын аты же кесип экендигин аныктайбыз 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
Ошентип, бул маселе чечилди , бирок татаалдыгы жогору экенин байкай аласыз. Демек, тексттик маалыматты атрибуттарда сактоо жеке элементтерге караганда көбүрөөк туура болот деген тыянак чыгарууга болот. Жана дагы бир таттуу тапшырма JavaRush программасында HTMLде элемент жөнүндө маалыматты көрсөтүү маселесин жарым-жартылай чечет, болгону аны бир аз оңдоо керек, бул жерде биз жөн гана элементтин ичиндеги бардык элементтерди тизмектейбиз :) Тапшырма №3 - элемент элементи берилгенде, бардык ички элементтердин аталыштарын жана атрибуттарын көрсөтүңүз, эгерде элемент табылбаса, аны көрсөтүңүз. Бул тапшырма үчүн биз төмөнкү 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. Андан кийин программа ичиндеги бардык элементтер жөнүндө бардык маалыматты көрсөтөт. Муну кантип кыла алабыз? Бул абдан жөнөкөй: биз жөн гана логикалык өзгөрмө жарыялашыбыз керек isEntered, ал бизге ичиндеги элемент керекпи же жокпу көрсөтөт, ал эми ичинде болсо, бардык маалыматтарды окуйбуз startElement. Чечим codeу:
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до бизге маалымат керек болгон элементти киргизип жатканда, желекти isEnteredtrue деп коёбуз, бул биз элементтин ичинде экенибизди билдирет. Элементтин ичине кирээрибиз менен, биз жөн гана ар бир жаңы элементти иштетебиз startElement, ал так биздин элементтин ички элементи экенин билебиз. Ошентип, биз элементтин атын жана анын аталышын чыгарабыз. Эгер элемент файлдан табылган жок болсо, анда бизде isFoundэлемент табылганда орнотулган өзгөрмө бар, ал эми ал жалган болсо, элемент табылган жок деген билдирүү пайда болот. Көрүнүп тургандай, мисалда биз конструкторго элементти 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
Ошентип, биз ички элементтер жана алардын атрибуттары жөнүндө бардык маалыматтарды алдык. Маселе чечилди. <h2>Эпилог</h2>Сиз SAX абдан кызыктуу инструмент жана абдан эффективдүү экенин көрдүңүз жана аны ар кандай жолдор менен, ар кандай максаттарда жана башкалар менен колдонсо болот, сиз жөн гана көйгөйдү оң тараптан карашыңыз керек. жагы, №2 жана №3 тапшырмада көрсөтүлгөндөй, анда SAX көйгөйдү чечүүнүн түздөн-түз ыкмаларын берген эмес, бирок биздин тапкычтыгыбыздын аркасында биз кырдаалдан чыгуунун жолун таба алдык. Макаланын кийинки бөлүгү толугу менен DOMга арналат. SAX менен таанышуу сизге жакты деп үмүттөнөм. Эксперимент, практика жана сиз баары абдан жөнөкөй экенин түшүнөт. Болгону, программалооңузга ийгorк жана жакында DOM жөнүндө бөлүгүн күтөбүз. Окууңарга ийгorк :) Мурунку макала: [Конкурс] Java программист үчүн XML негиздери - 3-бөлүктүн 2-бөлүгү Кийинки макала: [Конкурс] Java программист үчүн XML негиздери - 3-бөлүктөн 3.2 - DOM
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION