Kirish Hali so'nggi bo'lmagan maqolamning barcha o'quvchilariga salom va men sizni tabriklamoqchiman: XML haqidagi murakkab narsalar ortda qoldi. Ushbu maqola Java-da kodni o'z ichiga oladi. Bir oz nazariya bo'ladi, keyin esa amaliyot. SAX-dagi bitta material Word-da 10 sahifani to'ldirganligi sababli, men chegaralarga sig'a olmasligimni angladim. Shuning uchun, 3-modda, qanchalik g'alati tuyulmasin, 3 ta alohida maqolaga bo'linadi. Hammasi shu tartibda bo'ladi: SAX -> DOM -> JAXB. Ushbu maqola faqat SAXga qaratilgan. PS Kursning biron bir joyida HTML faylidagi barcha ichki elementlarni ko'rsatish kerak bo'lgan vazifa bor edi. Ushbu maqoladan so'ng, siz buni oddiy va murakkab ishlov berish algoritmlari bilan satr-satr o'qimasdan qilishingiz mumkin
BufferedReader
, shuningdek, shunga o'xshash yechim oxirgi amaliy misolda keltirilgan. Keling, boshlaymiz :) SAX (Simple API for XML) - NAZARIYA SAX ishlov beruvchisi shunday tuzilganki, u oddiygina XML fayllarni ketma-ket o'qiydi va turli hodisalarga reaksiyaga kirishadi, shundan so'ng u ma'lumotlarni maxsus hodisa ishlov beruvchisiga uzatadi. Unda juda ko'p voqealar mavjud, ammo eng tez-tez uchraydigan va foydalilari quyidagilar:
startDocument
— hujjatning boshiendDocument
- hujjatning oxiristartElement
- elementni ochishendElement
- elementni yopishcharacters
— elementlar ichidagi matn ma'lumotlari.
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;
Endi, birinchi navbatda, biz SAXParser yaratishimiz kerak:
public class SAXExample {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
// Creation фабрики и образца parserа
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
}
}
Ko'rib turganingizdek, siz avval zavod yaratishingiz kerak, keyin esa zavodda parserni o'zi yaratishingiz kerak. Endi bizda parserning o'zi bor, biz uning hodisalari uchun ishlov beruvchiga muhtojmiz. Buning uchun bizga qulaylik uchun alohida sinf kerak:
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 {
// Тут будет логика реакции на пустое пространство внутри элементов (пробелы, переносы строчек и так далее).
}
}
}
Biz nazariyada sanab o'tilgan voqealarni boshqarish uchun zarur bo'lgan barcha usullar bilan sinf yaratdik. Yana bir oz qo'shimcha nazariya: Haqida bir oz characters
: agar elementda matn bo'lsa, masalan, " salom " bo'lsa, nazariy jihatdan usulni har bir alohida belgi uchun ketma-ket 5 marta chaqirish mumkin, ammo bu unchalik muhim emas, chunki hamma narsa hali ham ishlaydi. va usullari haqida :startElement
endElement
uri
- bu element joylashgan bo'shliq, localName
- bu prefikssiz elementning nomi, qName
- bu prefiksli elementning nomi (agar mavjud bo'lsa, aks holda faqat nomi). elementdan). uri
va localName
agar biz zavodda bo'sh joyni qayta ishlashni yoqmagan bo'lsak, har doim bo'sh. Bu zavod usuli yordamida amalga oshiriladi setNamespaceAware(true)
. uri
Keyin bo'sh joy ( ) va ularning oldida prefiksli elementlarni olishimiz mumkin ( localName
). Vazifa №1 - Bizda quyidagi XML mavjud
<?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>
Bizning maqsadimiz: ushbu fayldan barcha xodimlar haqida barcha ma'lumotlarni olish. Birinchidan, biz sinf yaratishimiz kerakEmployee
:
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;
}
}
Va bizning asosiy sinfimizda SAXExample
barcha xodimlar ro'yxati kerak:
private static ArrayList<Employee> employees = new ArrayList<>();
Endi XML faylida bizga kerak bo'lgan ma'lumotlar qayerda ekanligini diqqat bilan ko'rib chiqamiz. Va, biz ko'rib turganimizdek, bizga kerak bo'lgan barcha ma'lumotlar elementlarning atributlari employee
. Va startElement
bizda bunday foydali parametr borligi sababli attributes
, bizda juda oddiy vazifa bor. Birinchidan, kodimizni chalkashtirib yubormaslik uchun keraksiz usullarni olib tashlaymiz. Bizga faqat kerak startElement
. Va usulning o'zida biz xodim yorlig'ining atributlaridan ma'lumot to'plashimiz kerak. Diqqat:
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));
}
}
}
}
Mantiq oddiy: agar element nomi bo'lsa employee
, biz shunchaki uning atributlari haqida ma'lumot olamiz. Foydali usul mavjud attributes
, bunda atribut nomini bilib, uning qiymatini olishingiz mumkin. Biz ishlatgan narsamiz. Endi biz elementning boshlanishi uchun hodisa ishlov beruvchisini yaratdik, biz XML faylimizni tahlil qilishimiz kerak . Buni amalga oshirish uchun buni bajaring:
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));
}
}
}
}
Tahlil qilish usulida siz xml fayli va siz yaratgan ishlov beruvchiga yo'lni o'tkazishingiz kerak. Shunday qilib, ushbu koddan foydalanib, biz ushbu XML dan ma'lumot oldik:
<?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>
Va biz quyidagi natijani oldik:
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
Missiya amalga oshirildi! Vazifa №2 - bizda quyidagi XML mavjud:
<?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>
Bizning maqsadimiz: ushbu fayldan barcha xodimlar haqida barcha ma'lumotlarni olish. Bu muammo noto'g'ri tuzilgan XML fayli kod yozishni qanday qiyinlashtirishi mumkinligini yaxshi ko'rsatib beradi. Ko'rib turganingizdek, ism va joylashuv haqidagi ma'lumotlar endi name
va elementlari ichida matn ma'lumoti sifatida saqlanadi job
. Elementlar ichidagi matnni o'qish uchun bizda belgilar usuli mavjud. Buning uchun biz mantiqiy takomillashtirilgan yangi ishlov beruvchi sinfini yaratishimiz kerak. Shuni unutmangki, ishlov beruvchilar har qanday murakkablikdagi mantiqni saqlashga qodir bo'lgan to'liq huquqli sinflardir. Shuning uchun, endi biz protsessorimizni sozlaymiz. Aslida, shuni ta'kidlash kifoyaki, biz har doim navbatma-navbat name
turamiz job
va qanday tartibda bo'lishimiz muhim emas, biz osongina ism va kasbni alohida o'zgaruvchilarga saqlashimiz mumkin va ikkala o'zgaruvchi ham saqlanganida, bizning xodimimizni yaratamiz. Faqat bu erda, elementning boshlanishi bilan bir qatorda, bizda element ichidagi matn uchun parametr yo'q. Biz matnda usullardan foydalanishimiz kerak. Ammo, agar ular butunlay boshqacha usullar bo'lsa, biz qanday qilib element ichidagi matn ma'lumotlarini olamiz? Mening yechimim: biz faqat oxirgi elementning nomini eslab qolishimiz va characters
ma'lumotni qaysi elementda o'qiyotganimizni tekshirishimiz kerak. Shuni ham yodda tutish kerakki, <codee>belgilar elementlar ichidagi barcha belgilarni o'qiydi, ya'ni barcha bo'shliqlar va hatto satr tanaffuslari o'qiladi. Va bizga ularga kerak emas. Biz bu maʼlumotlarni eʼtiborsiz qoldirishimiz kerak, chunki u notoʻgʻri.</codee> Kod:
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;
}
}
}
}
Ko'rib turganingizdek, XML fayl tuzilishining oddiy murakkabligi tufayli bizning kodimiz sezilarli darajada murakkablashdi. Biroq, kod murakkab emas. Tavsif: biz xodim ( name
, job
) haqidagi ma'lumotlarni saqlash uchun o'zgaruvchilarni , shuningdek, lastElementName
qaysi element ichida ekanligimizni yozib olish uchun o'zgaruvchini yaratdik. Shundan so'ng, usulda characters
biz ma'lumotni filtrlaymiz va agar u erda hali ham ma'lumot mavjud bo'lsa, bu bizga kerak bo'lgan matn ekanligini anglatadi va keyin bu ism yoki kasb ekanligini aniqlaymiz lastElementName
. Usulda endElement
biz barcha ma'lumotlar o'qilganligini tekshiramiz va agar shunday bo'lsa, biz xodimni yaratamiz va ma'lumotni qayta tiklaymiz. Yechimning chiqishi birinchi misolga teng:
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
Shunday qilib, bu muammo hal qilindi , lekin siz murakkabligi yuqori ekanligini sezishingiz mumkin. Shunday qilib, matn ma'lumotlarini atributlarda saqlash ko'pincha alohida elementlarga qaraganda to'g'riroq bo'ladi degan xulosaga kelishimiz mumkin. Va yana bir shirin vazifa, JavaRush-da HTML-da element haqidagi ma'lumotni ko'rsatish bo'yicha muammoni qisman hal qiladi, faqat uni biroz tahrirlash kerak bo'ladi, bu erda biz shunchaki element ichidagi barcha elementlarni sanab o'tamiz :) Vazifa №3 - element elementi berilgan bo'lsa, barcha ichki elementlarning nomlari va atributlarini ko'rsating; agar element topilmasa, buni ko'rsating. Ushbu vazifani bajarish uchun biz quyidagi XML faylidan foydalanamiz:
<?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>
Ko'rib turganingizdek, bizda uchta mumkin bo'lgan stsenariy mavjud: root
, mysql
, oracle
. Keyin dastur ichidagi barcha elementlar haqidagi barcha ma'lumotlarni ko'rsatadi. Buni qanday qilishimiz mumkin? Va bu juda oddiy: biz faqat mantiqiy o'zgaruvchini e'lon qilishimiz kerak isEntered
, bu bizga element kerak yoki yo'qligini ko'rsatadi va agar ichkarida bo'lsa, barcha ma'lumotlarni o'qing startElement
. Yechim kodi:
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;
}
}
}
Ushbu kodda bizga ma'lumot kerak bo'lgan elementni kiritishda biz bayroqni isEntered
true ga o'rnatamiz, ya'ni biz element ichidamiz. Va biz elementning ichida bo'lganimizdan so'ng, biz har bir yangi elementni qayta ishlaymiz startElement
, chunki u bizning elementimizning ichki elementi ekanligini bilib olamiz. Shunday qilib, biz element nomini va uning sarlavhasini chiqaramiz. Agar element faylda topilmagan bo'lsa, bizda isFound
element topilganda o'rnatiladigan o'zgaruvchi mavjud va agar u noto'g'ri bo'lsa, element topilmaganligi haqida xabar paydo bo'ladi. Ko'rib turganingizdek, misolda biz konstruktorga elementni SearchingXMLHandler
uzatdik . root
Uning uchun xulosa:
Найден элемент <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
Shunday qilib, biz ichki elementlar va ularning atributlari haqida barcha ma'lumotlarni oldik. Muammo hal qilindi. <h2>Epilog</h2>Siz ko'rdingizki, SAX juda qiziqarli vosita va juda samarali va u turli yo'llar bilan, turli maqsadlarda va hokazolarda ishlatilishi mumkin, muammoga faqat o'ng tomondan qarash kerak. tomoni, № 2 va 3-sonli topshiriqlarda ko'rsatilgandek, bu erda SAX muammoni hal qilishning to'g'ridan-to'g'ri usullarini taqdim etmagan, ammo bizning zukkoligimiz tufayli biz vaziyatdan chiqish yo'lini topa oldik. Maqolaning keyingi qismi butunlay DOMga bag'ishlanadi. Umid qilamanki, sizga SAX bilan tanishish yoqdi. Tajriba, mashq va hamma narsa juda oddiy ekanligini tushunasiz. Hammasi shu, dasturlashda omad tilaymiz va tez orada DOM haqidagi qismni kutamiz. O'qishlaringizga omad :) Oldingi maqola: [Raqobat] Java dasturchisi uchun XML asoslari - 2/3 qism Keyingi maqola: [Raqobat] Java dasturchisi uchun XML asoslari - 3 qismning 3.2 - DOM
GO TO FULL VERSION