1. DOM (Document Object Model)
Bəzən XML faylı o qədər böyük olur ki, onu tam şəkildə yaddaşa yükləmək mümkün olmur. Elə hallar da var ki, sənədin strukturu əvvəlcədən məlum deyil və ya həddən artıq mürəkkəbdir. Başqa situasiyalarda isə bütün faylı yox, yalnız ayrı-ayrı hissələri emal etmək kifayətdir və ya proqram işləyən zaman sənədi dəyişmək lazımdır: düyünü silmək, yeni element əlavə etmək və ya strukturun bir hissəsini yenidən qurmaq.
Belə situasiyalarda klassik və sınanmış alətlər — DOM və SAX — uyğundur. Onlar XML məzmunu ilə birbaşa işləməyə imkan verir və əvvəlcədən təsvir edilmiş Java siniflərinə bağlılıq tələb etmir.
DOM necə işləyir
DOM — XML sənədini yaddaşda obyektlər ağacı kimi təqdim etmə üsuludur. Hər bir teq ağacın bir düyününə (Node) çevrilir, atributlar, mətn dəyərləri və hətta şərhlər də ayrıca obyektlər olur. Sənədi yüklədikdən sonra onun strukturuna tam çıxış əldə edirsiniz: element və atributları oxumaq, dəyişmək, silmək, əlavə etmək mümkündür.
Javada DOM-un əsas sinifləri
- DocumentBuilderFactory — parser yaratmaq üçün fabrik.
- DocumentBuilder — XML-i ağaca çevirən parser.
- Document — ağacın kök obyekti.
- Element — XML elementi (teq).
- NodeList — düyünlər siyahısı (məsələn, <items> daxilindəki bütün <item> elementləri).
Nümunə: DOM ilə XML faylının oxunması
Tutaq ki, belə bir XML faylını oxumaq lazımdır:
<contacts>
<person id="1">
<name>Ivan</name>
<email>ivan@example.com</email>
</person>
<person id="2">
<name>Mariya</name>
<email>maria@example.com</email>
</person>
</contacts>
Kod: Elementlərin oxunması və keçilməsi
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.File;
public class DomExample {
public static void main(String[] args) throws Exception {
// 1. Fabriki və parseri yaradırıq
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. XML faylını yaddaşa yükləyirik
Document doc = builder.parse(new File("contacts.xml"));
// 3. Kök elementi əldə edirik
Element root = doc.getDocumentElement();
System.out.println("Kök element: " + root.getTagName());
// 4. Bütün <person> elementlərinin siyahısını alırıq
NodeList persons = root.getElementsByTagName("person");
for (int i = 0; i < persons.getLength(); i++) {
Element person = (Element) persons.item(i);
String id = person.getAttribute("id");
String name = person.getElementsByTagName("name").item(0).getTextContent();
String email = person.getElementsByTagName("email").item(0).getTextContent();
System.out.println("id: " + id + ", name: " + name + ", email: " + email);
}
}
}
Burada nə baş verir:
- Öncə parser yaradırıq və XML faylını yükləyirik.
- Kök elementi (contacts) əldə edirik.
- Bütün <person> elementlərini tapıb üzərindən keçirik.
- Hər bir <person> üçün id atributunu, <name> və <email> elementlərini oxuyuruq.
Nümunə üçün DOM ağacının sxemi
contacts
├── person (id="1")
│ ├── name ("Ivan")
│ └── email ("ivan@example.com")
└── person (id="2")
├── name ("Mariya")
└── email ("maria@example.com")
DOM ilə XML-in dəyişdirilməsi
DOM yalnız oxumağa yox, XML-i dəyişməyə də imkan verir. Məsələn, yeni bir şəxsi əlavə edək:
// Yeni <person> elementi yaradırıq
Element newPerson = doc.createElement("person");
newPerson.setAttribute("id", "3");
// <name>
Element name = doc.createElement("name");
name.setTextContent("Sergey");
newPerson.appendChild(name);
// <email>
Element email = doc.createElement("email");
email.setTextContent("sergey@example.com");
newPerson.appendChild(email);
// Kökə əlavə edirik
root.appendChild(newPerson);
// Dəyişiklikləri fayla yazırıq
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(new DOMSource(doc), new StreamResult(new File("contacts-updated.xml")));
DOM-un əsas üstünlükləri və çatışmazlıqları
- Üstünlüklər: kiçik fayllar üçün rahatdır, istənilən dəyişiklikləri etmək asandır, ağacda istənilən istiqamətdə hərəkət etmək mümkündür.
- Mənfi cəhətlər: bütün XML yaddaşda saxlanılır. Çox böyük fayllar (yüzlərlə MB və daha çox) üçün uyğun deyil, yaddaş tez tükənə bilər.
2. SAX (Simple API for XML)
SAX hadisə əsaslı parserdir. O, ağac qurmur, sadəcə XML-i “soldan sağa” oxuyur və hadisə işləyicilərini çağırır: “element başladı”, “mətn məzmunu”, “element bitdi” və s. Siz öz işləyicinizi yazır və lazım olan hadisələrə reaksiya verirsiniz.
Analoji:
Əgər DOM — bütün planlayıcı dəftərin səhifələrini stolun üzərinə sərmək kimidirsə, SAX — bu dəftəri səhifə-səhifə oxuyub, yalnız lazım olan səhifəni görəndə qeyd aparmaq kimidir.
Javada SAX-ın əsas sinifləri
- SAXParserFactory, SAXParser — fabrik və parser.
- DefaultHandler — hadisələri emal etmək üçün əsas sinif.
- Metod-işləyicilər: startElement, characters, endElement, startDocument, endDocument.
Nümunə: SAX ilə XML faylının oxunması
Tutaq ki, elə həmin contacts.xml faylıdır. Sadəcə bütün insanların adlarını və e-poçtlarını çıxarmaq istəyirik.
Kod: SAX parseri
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import java.io.File;
public class SaxExample {
public static void main(String[] args) throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
parser.parse(new File("contacts.xml"), new ContactHandler());
}
}
class ContactHandler extends DefaultHandler {
private String currentElement = "";
private String name = "";
private String email = "";
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
currentElement = qName;
if ("person".equals(qName)) {
String id = attributes.getValue("id");
System.out.println("Yeni şəxs, id: " + id);
}
}
@Override
public void characters(char[] ch, int start, int length) {
String text = new String(ch, start, length).trim();
if (text.isEmpty()) return;
if ("name".equals(currentElement)) {
name = text;
} else if ("email".equals(currentElement)) {
email = text;
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("person".equals(qName)) {
System.out.println("Ad: " + name + ", email: " + email);
name = "";
email = "";
}
currentElement = "";
}
}
İndi bunu sadə dillə izah edək. SAX parseri açılış teqini, məsələn <person> görəndə, startElement metodunu çağırır. Orada biz dərhal id atributunu oxuyur və ekrana çıxarırıq. Daxildə mətn — məsələn, ad və ya e-poçt — görünəndə idarəetmə characters metoduna keçir və biz həmin mətni müvəqqəti dəyişənlərdə saxlayırıq. Parser </person> bağlanış teqinə çatanda endElement çağırılır. Bu anda artıq şəxsin adını və e-poçtunu bilirik və onları çap edə bilirik. Sonra dəyişənlər növbəti kontakt üçün sıfırlanır.
Fikir ondadır ki, SAX bütün XML-i yaddaşda saxlamır, “axın oxuyucusu” kimi işləyir: faylı yuxarıdan aşağıya keçərək hadisələrə reaksiya verir — teqin başlanğıcı, mətn, teqin sonu. Bu, xüsusilə böyük fayllar üçün sürətli və yaddaşa qənaətcildir.
DOM və SAX-ın qısa müqayisəsi
| DOM | SAX | |
|---|---|---|
| Üslub | Ağac (bütün struktur yaddaşda) | Hadisələr (“yerindəcə” emal) |
| Yaddaş | Bütün XML-i yükləyir | Minimum yaddaş istifadəsi |
| Dəyişikliklər | Oxuma/dəyişdirmə/əlavə etmə mümkündür | Yalnız oxuma (adətən) |
| Məlumat axtarışı | İstənilən yerdə axtarmaq asandır | “İndi haradasan” vəziyyətini izləmək lazımdır |
| Faylın ölçüsü | Kiçik və orta ölçülü fayllar üçün | Çox böyük fayllar üçün |
3. DOM-u nə vaxt, SAX-ı nə vaxt istifadə etməli
DOM aşağıdakı hallarda uyğundur:
- Fayl kiçik və ya orta ölçülüdür.
- XML-in müxtəlif hissələrinə dəfələrlə müraciət etmək lazımdır.
- Sənədin strukturunu dəyişdirmək tələb olunur.
SAX — ən doğru seçimdir, əgər:
- Fayl nəhəngdir və onu yaddaşa yükləmək mümkün deyil.
- Yalnız məlumatın bir hissəsini tez “çıxarmaq” lazımdır (məsələn, müəyyən atributa malik bütün <item> elementlərini tapmaq).
- Yüksək performans və minimum yaddaş istifadəsi tələb olunur.
- XML-i dəyişdirməyi planlaşdırmırsınız, yalnız oxuyacaqsınız.
Məsləhət:
Real layihələrdə tez-tez hər iki yanaşmadan istifadə olunur: DOM — “insan üçün” tənzimləmələr və kiçik konfiqlər üçün, SAX — loqların, ixracların, böyük importların emalı üçün.
4. Praktiki tapşırıq: tətbiq üçün kiçik parser
Tədris tətbiqinizdə (məsələn, “kontakt kitabı”) deyək ki, belə bir vəzifə yarandı: gmail.com domenli e-poçt ünvanı olan istifadəçilərin sayını tez hesablamaq.
DOM həlli:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("contacts.xml"));
NodeList emails = doc.getElementsByTagName("email");
int count = 0;
for (int i = 0; i < emails.getLength(); i++) {
String email = emails.item(i).getTextContent();
if (email.endsWith("@gmail.com")) {
count++;
}
}
System.out.println("gmail.com domenli istifadəçilərin sayı: " + count);
SAX həlli:
class GmailCounterHandler extends DefaultHandler {
private String currentElement = "";
int count = 0;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
currentElement = qName;
}
@Override
public void characters(char[] ch, int start, int length) {
if ("email".equals(currentElement)) {
String email = new String(ch, start, length).trim();
if (email.endsWith("@gmail.com")) {
count++;
}
}
}
@Override
public void endDocument() {
System.out.println("gmail.com domenli istifadəçilərin sayı: " + count);
}
}
5. DOM və SAX ilə işləməyin xüsusiyyətləri və incəlikləri
- DOM XML faylı çox böyük olduqda yaddaşı tükədə bilər. Əgər OutOfMemoryError xətasını görsəniz, çox güman ki, SAX-a keçməyin vaxtıdır.
- SAX diqqət tələb edir: hansı elementin daxilində olduğunuzu izləmək və lazım olan məlumatı dəqiq toplamaq lazımdır. Bəzən dərin iç-içə strukturlarda çaşmamaq üçün stek və ya əlavə dəyişənlərdən istifadə etmək gərəkir.
- SAX-da characters metodu eyni mətn blokuna bir neçə dəfə çağırıla bilər (xüsusilə mətn uzun olduqda və ya xüsusi simvollar olduqda). Mətni StringBuilder-də toplamaq daha yaxşıdır.
- DOM axtarış, naviqasiya və strukturu dəyişdirmək üçün əladır, lakin axın emalı üçün uyğun deyil.
- Hansını seçəcəyinizə əmin deyilsinizsə — sadəlik üçün DOM-la başlayın, yaddaş “ağırlaşdıqda” SAX-a keçin.
6. DOM və SAX ilə işləyərkən tipik səhvlər
Səhv №1: SAX-da boşluq və sətirsonlarının yanlış emalı.
characters metodu mətn parçalarını, o cümlədən elementlər arasındakı boşluqları və sətirsonlarını qaytara bilər. Əgər .trim().isEmpty() filtrindən istifadə etməsəniz, çoxlu “boş” çağırışlar əldə edə və ya mətni səhv toplaya bilərsiniz.
Səhv №2: SAX vasitəsilə XML-i dəyişməyə cəhd.
SAX — yalnız oxumaq üçündür! Strukturu dəyişmək lazımdırsa — DOM istifadə edin.
Səhv №3: SAX-da hadisələrin ardıcıllığını pozmaq.
Əgər endElement-də dəyişənlər sıfırlanmasa, elementlər arasında “məlumat sızması” baş verə bilər.
Səhv №4: Nəhəng fayllar üçün DOM-dan istifadə.
Nəticə — OutOfMemoryError və ya çox ləng işləmə.
Səhv №5: DOM-da tiplərin yanlış çevrilməsi.
DOM-da hər şey Node-dur, lakin atributlar və övlad elementlərlə işləmək üçün Element-ə çevirmək lazımdır. Yanlış çevirmə ClassCastException-a səbəb ola bilər.
GO TO FULL VERSION