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

Java бағдарламашысына арналған XML негіздері – 3/3.1 бөлімі – SAX

Топта жарияланған
Кіріспе Менің әлі соңғы емес мақаламның барлық оқырмандарына сәлем, мен сізді құттықтағым келеді: XML туралы күрделі материал артта қалды. Бұл мақалада Java тіліндегі code болады. Кішкене теория, содан кейін практика болады. SAX-тағы бір материал Word бағдарламасында 10 бетті толтырғандықтан, мен шектеулерге төтеп бере алмайтынымды түсіндім. Сондықтан 3-баптың өзі қаншалықты оғаш көрінсе де, 3 бөлек бапқа бөлінеді. Барлығы осы ретпен болады: SAX -> DOM -> JAXB. Бұл мақала тек SAX тақырыбына арналған. PS Курста бір жерде HTML файлындағы барлық ішкі элементтерді көрсету қажет тапсырма болды. Осы мақаладан кейін сіз мұны қарапайым және күрделі өңдеу алгоритмдерімен қатар оқымай-ақ жасай аласыз BufferedReader, сонымен қатар ұқсас шешім соңғы практикалық мысалда беріледі. Бастайық :) SAX (XML үшін қарапайым API) - ТЕОРИЯ 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: егер элементте мәтін болса, мысалы, « сәлем », онда теориялық тұрғыдан әдісті әрбір жеке таңба үшін қатарынан 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Атрибуттың атын біле отырып, оның мәнін алуға болатын пайдалы әдіс бар . Міне, біз пайдаландық. Енді элементтің басы үшін оқиға өңдегішін жасағандықтан, 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> Код:
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та бізге ақпарат қажет элементті енгізу кезінде біз жалаушаны isEnteredшын мәніне қоямыз, яғни біз элементтің ішінде екенімізді білдіреді. Элементтің ішінде болғаннан кейін біз әрбір жаңа элементтің 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-пен танысу сізге ұнады деп үміттенемін. Эксперимент, тәжірибе және сіз бәрі өте қарапайым екенін түсінесіз. Мұның бәрі, бағдарламалауға сәттілік тілейміз және жақын арада DOM туралы бөлімді күтеміз. Оқуларыңызға сәттілік :) Алдыңғы мақала: [Байқау] Java бағдарламашысына арналған XML негіздері - 3 бөлімнің 2-ші бөлімі Келесі мақала: [Байқау] Java бағдарламашысына арналған XML негіздері - 3 бөлігінің 3.2-бөлімі - DOM
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION