JavaRush /مدونة جافا /Random-AR /أساسيات XML لمبرمج Java - الجزء 3.1 من 3 - SAX
Ярослав
مستوى
Днепр

أساسيات XML لمبرمج Java - الجزء 3.1 من 3 - SAX

نشرت في المجموعة
مقدمة مرحبًا بجميع قراء مقالتي التي لم تكن الأخيرة بعد، وأريد أن أهنئكم: لقد تجاوزنا الأمور المعقدة المتعلقة بـ XML. ستحتوي هذه المقالة على تعليمات برمجية بلغة Java. سيكون هناك القليل من النظرية، ثم الممارسة. ونظرًا لأن قطعة واحدة من المادة الموجودة على SAX تملأ 10 صفحات في Word، فقد أدركت أنني لا أستطيع الالتزام بالحدود. لذلك، سيتم تقسيم المادة 3 إلى 3 مواد منفصلة، ​​مهما بدا الأمر غريبا. كل شيء سيكون بهذا الترتيب: SAX -> DOM -> JAXB. سوف تركز هذه المقالة فقط على SAX. ملاحظة: كانت هناك مهمة في مكان ما من الدورة حيث كان من الضروري عرض جميع العناصر الداخلية في ملف HTML. بعد هذه المقالة، ستتمكن من القيام بذلك دون قراءة سطرًا تلو الآخر مع BufferedReaderخوارزميات المعالجة التقليدية والمعقدة، كما سيتم تقديم حل مماثل في المثال العملي الأخير. هيا بنا نبدأ :) SAX (واجهة برمجة تطبيقات بسيطة لـ XML) - النظرية تم تصميم معالج SAX بحيث يقرأ ملفات XML بشكل تسلسلي ويتفاعل مع الأحداث المختلفة، وبعد ذلك يقوم بتمرير المعلومات إلى معالج حدث خاص. لديها عدد غير قليل من الأحداث، ولكن الأكثر شيوعا ومفيدة هي ما يلي:
  1. startDocument- بداية الوثيقة
  2. endDocument- نهاية الوثيقة
  3. startElement- فتح عنصر
  4. endElement- إغلاق عنصر
  5. characters- معلومات نصية داخل العناصر.
تتم معالجة كافة الأحداث في معالج الأحداث ، الذي يجب إنشاؤه وتجاوز الأساليب . المزايا: أداء عالي بسبب الطريقة "المباشرة" لقراءة البيانات وتكاليف الذاكرة المنخفضة. العيوب: وظائف محدودة، مما يعني أنه في المسائل غير الخطية سيتعين علينا تحسينها. SAX (واجهة برمجة التطبيقات البسيطة لـ XML) – الممارسة قم بإدراج قائمة الواردات على الفور حتى لا تبحث عن أي شيء وتخلط بينه:
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 مرات متتالية لكل حرف على حدة، لكن هذا ليس مشكلة كبيرة، لأن كل شيء سيظل يعمل. حول startElementوالطرق endElement:uri - هذه هي المساحة التي يوجد بها العنصر، 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، فلدينا مهمة بسيطة إلى حد ما. أولاً، دعونا نزيل الأساليب غير الضرورية حتى لا نتسبب في فوضى التعليمات البرمجية الخاصة بنا. نحتاج فقط إلى 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 والمعالج الذي قمت بإنشائه. وهكذا، باستخدام هذا الكود، قمنا باستخراج المعلومات من ملف 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 سيئ التنظيم أن يجعل كتابة التعليمات البرمجية أكثر صعوبة. كما ترون، يتم الآن تخزين المعلومات المتعلقة بالاسم والمنصب كمعلومات نصية داخل العناصر 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، أصبح الكود الخاص بنا أكثر تعقيدًا بشكل ملحوظ. ومع ذلك، فإن التعليمات البرمجية ليست معقدة. الوصف: قمنا بإنشاء متغيرات لتخزين البيانات الخاصة بالموظف ( 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. رمز الحل:
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;
        }
    }
}
في هذا الكود، عند إدخال عنصر نحتاج إلى معلومات عنه، نقوم بتعيين العلامة isEnteredعلى true، مما يعني أننا داخل العنصر. وبمجرد أن نكون داخل العنصر، نقوم ببساطة بمعالجة كل عنصر جديد 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. أتمنى أنك استمتعت بالتعرف على ساكس. قم بالتجربة والممارسة وسوف تفهم أن كل شيء بسيط للغاية. وهذا كل شيء، حظًا سعيدًا في برمجتك ونتطلع إلى الجزء الخاص بـ DOM قريبًا. حظًا سعيدًا في دراستك :) المقالة السابقة: [المنافسة] أساسيات XML لمبرمج Java - الجزء 2 من 3 المقالة التالية: [المنافسة] أساسيات XML لمبرمج Java - الجزء 3.2 من 3 - DOM
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION