الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

قراءة المستندات Xml باستعمال Sax

بدأه herch في 11 يناير 2010 · 18 رد · 5,203 مشاهدة · في JavaSE
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم،

الSAX أو Simple API for XML، هي API توفرها الجافا لقراءة المستندات xml.

  • الفرق بين SAX و DOM API
  • كيف يتم التنصت على أحداث الSAX
  • كتابة الفئة التي ستتنصت على الأحداث
  • الفئة XMLReader
  • البرنامج بأكمله
  • استرجاع قيمة عنصر ما

*******************************************************************************************


الفرق بين SAX و DOM API
الحزمتين SAX و DOM لهما نفس الهدف، وهو قراءة المستندات xml، لكن الوسيلة هي التي تختلف، وهذا هو ما يحدد اي حزمة سنستعمل.
في حالة DOM، فإنه يتم عمل parsing للمستند وإنشاء شجرة tree في الذاكرة تمثل المستند xml، ميزة هذه الطريقة انها تسمح لنا ب random-access، أي أننا نستطيع استرجاع أي معلومة من أي عنصر في المستند في أي وقت. عيب هذه الطريقة هو أنها تستهلك موارد كبيرة من الذاكرة إذا كان المستند xml كبير الحجم.
الSAX يعتبر event-based API، هذا يعني أنه يكتفي فقط بإرسال أحداث إلى البرنامج، ولا يخزن أي شيء، وبالتالي فهو لا يحتاج لموارد كبيرة من الذاكرة، لكنه وبخلاف DOM لا يتيح random-access لعناصر المستند xml، فهو يمر مرة واحدة على كل عنصر ما حسب ترتيبه في المستند، وبعد ذلك لا يمكن العودة إليه، لذلك يتوجب على المبرمج أن يخزن قيمة العناصر التي يحتاج.
لمزيد من المعلومات حول الفرق بين SAX و DOM
http://www.w3.org/DO....html#SAXandDOM
كيف يتم التنصت على أحداث الSAX
بما أن SAX يعتبر event-based، فيلزمنا فئة تقوم بالتنصت على هاته الأحداث، وتنفيذ العمليات التي نريد عند كل حدث. هذه الفئة يجب أن تعمل implement للinterface ContentHandler، هاته الأخيرة تحتوي على عدة طرق، كل واحدة منها تمثل حدثا SAX، من أهم هاته الطرق نجد startDocument، endDocument، startElement، endElement، characters

void startDocument() throws SAXException


هاته الطريقة يتم تنفيذها عند بداية المستند

void endDocument() throws SAXException


هاته الطريقة يتم تنفيذها عند نهاية المستند

void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException


هاته الطريقة يتم تنفيذها عند بداية كل عنصر (node) من المستند.

  • المعطى "uri" يمثل مسار الnamespace الذي ينتمي له العنصر، في حالة لم يكن ينتمي لأي namespace، فإن المعطى يكون جملة فارغة.
  • المعطى "localName" يمثل إسم العنصر.
  • المعطى "qName" يمثل إسم العنصر بالإضافة إلى الprefix الخاص بالnamespace، في حالة لم يكن ينتمي لأي namespace، فإن المعطى يكون جملة فارغة.
  • المعطى "atts" يمثل الattributes المرتبطة بهذا العنصر.

void endElement(String uri, String localName, String qName) throws SAXException

هاته الطريقة يتم تنفيذها عند نهاية كل عنصر.

public void characters(char[] ch, int start, int length) throws SAXException

هاته الطريقة يتم تنفيذها عند العثور على أي نص.

كتابة الفئة التي ستتنصت على الأحداث

لتسهيل الأمر، لن نقوم بعمل implement لContentHandler، لكننا سنرث من فئة تقوم بذلك، وهي DefaultHandler، وهكذا سنكتب فقط الطرق التي نريد استعمالها.

class MyHandler extends DefaultHandler {

	@Override
	public void startDocument() throws SAXException {
		System.out.println("Start document.");
	}

	@Override
	public void endDocument() throws SAXException {
		System.out.println("End document.");
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		System.out.println("Start element " + localName +".");		
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		System.out.println("End element " + localName +".");
	}
}

الفئة XMLReader

هاته الفئة هي الparser، فهي التي تقرأ المستند xml، وهي أيضا التي ترسل الأحداث SAX، وبالتالي سنريطها مع الفئة MyHandler التي كتبنا والتي تتنصت على هاته الأحداث.

لإنشاء كائن XMLReader ننادي على الطريقة

XMLReaderFactory.createXMLReader();

لربط هذا الكائن بالفئة التي تتنصت على الأحداث، نستعمل الطريقة

setContentHandler (ContentHandler handler)

وأخيرا نبدأ بقراءة المستند xml بواسطة الطريقة parse التي نمرر لها مسار المستند xml

parse (String systemId) throws IOException, SAXException

البرنامج بأكمله

import java.io.IOException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class TestSAX {

	public static void main(String[] args) {
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			reader.setContentHandler(new MyHandler());
			reader.parse("addressbook.xml");
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (SAXException ex) {
			ex.printStackTrace();
		}
	}
}

class MyHandler extends DefaultHandler {

	@Override
	public void startDocument() throws SAXException {
		System.out.println("Start document.");
	}

	@Override
	public void endDocument() throws SAXException {
		System.out.println("End document.");
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		System.out.println("Start element " + localName +".");
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		System.out.println("End element " + localName +".");
	}
}

والمستند addressbook.xml

<?xml version="1.0" encoding="UTF-8"?>

<phonebook>
	<person category="friend">
		<name>Ahmed</name>
		<age>23</age>
		<sexe>male</sexe>
		<phone>0522222222</phone>
		<address>182 rue yyy</address>
	</person>
	<person category="friend">
		<name>Sophia</name>
		<age>24</age>
		<sexe>female</sexe>
		<phone>0522333333</phone>
		<address>16 yyy street</address>
	</person>
	<person category="neighbour">
		<name>Ali</name>
		<age>30</age>
		<sexe>male</sexe>
		<phone>0640444444</phone>
		<address>172 rue yyy</address>
	</person>
	<person category="neighbour">
		<name>Samir</name>
		<age>28</age>
		<sexe>male</sexe>
		<phone>0660666666</phone>
		<address>180 rue yyy</address>
	</person>
</phonebook>

لا بد أنكم لاحظتم أنه لا توجد أي طريقة مباشرة لاسترجاع محتوى كل عنصر. ذلك لان المحتوى يعتبر كنص، وبالتالي فيمكننا استرجاعه في الطريقة characters التي سبق وأشرنا إليها، لكن يجب قبل ذلك معرفة إلى أي عنصر ينتمي هذا النص، لانه لا توجد وسيلة مباشرة لمعرفة ذلك داخل الطريقة characters.

استرجاع قيمة عنصر ما

الفكرة بسيطة، وهي أننا سنقوم بتخزين إسم العنصر الحالي داخل الطريقة startElement في متغير، نسميه مثلا currentElement، وداخل الطريقة characters، نتحقق من قيمة المتغير، فإذا كان يساوي العنصر الذي نريده، فإننا نسترجع النص الذي تم تمريره إلى الطريقة characters.

نقوم أولا بالإعلان عن المتغير currentElement

private String currentElement;

داخل الطريقة startElement نقوم بتخزين إسم العنصر الحالي

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
	System.out.println("Start element " + localName +".");
	currentElement = localName;
}

وداخل الطريقة endElement، نجعل قيمة currentElement مساوية ل null، لأننا خرجنا من العنصر

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
	System.out.println("End element " + localName +".");
	currentElement = null;
}

وفي الطريقة characters، نقوم بالتحقق من قيمة المتغير currentElement، ونقوم بالعمل الذي نريد حسب العنصر الحالي، في المثال نقوم فقط بكتابة إسم كل شخص

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
	String value = new String(ch, start, length);
	if ("name".equals(currentElement)) {
		System.out.println("Name: " + value);
	} 
}

البرنامج بعد التعديل:

import java.io.IOException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class TestSAX {

	public static void main(String[] args) {
		try {
			XMLReader reader = XMLReaderFactory.createXMLReader();
			reader.setContentHandler(new MyHandler());
			reader.parse("addressbook.xml");
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (SAXException ex) {
			ex.printStackTrace();
		}
	}
}

class MyHandler extends DefaultHandler {

	private String currentElement;

	@Override
	public void startDocument() throws SAXException {
		System.out.println("Start document.");
	}

	@Override
	public void endDocument() throws SAXException {
		System.out.println("End document.");
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		System.out.println("Start element " + localName +".");
		currentElement = localName;
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		System.out.println("End element " + localName +".");
		currentElement = null;
	}

	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		String value = new String(ch, start, length);
		if ("name".equals(currentElement)) {
			System.out.println("Name: " + value);
		}
	}
}

تم تعديل هذه المشاركة بواسطة herch في 11 يناير 2010 في 20:34

2
#2

إضافه :

أيضاً يفضل استخدام الـ SAX مع الملفات الكبيرة التي تحتوي علي elements كثيرة جدا

غير ذلك فالـ DOM كفيل بالعمل بكفاءة ناهيك عن انه ابسط

الـ xml parsing

حقيقي من الموضوعات الممتعه في الجافا

صــفــر

#3
herch كتب:

في حالة DOM، فإنه يتم عمل parsing للمستند وإنشاء شجرة tree في الذاكرة تمثل المستند xml، ميزة هذه الطريقة انها تسمح لنا ب random-access، أي أننا نستطيع استرجاع أي معلومة من أي عنصر في المستند في أي وقت. عيب هذه الطريقة هو أنها تستهلك موارد كبيرة من الذاكرة إذا كان المستند xml كبير الحجم.

عندما قلت مستند كبير الحجم، كنت أعني أنه يحتوي على عناصر كثيرة :). تقريبا تلك هي الحالة التي يستحسن فيها عدم استعمال DOM واستبداله بSAX.

معك حق، الxml parsing موضوع ممتع ومشوق :happy:.

تم تعديل هذه المشاركة بواسطة herch في 11 يناير 2010 في 03:16

#4

oops

أسف أستاذ herch

لم أستوعبها جيداً

جزاك الله خيرا :)

###########

بالمناسبه مشروع تخرجي يعتمد بشكل أساسي علي XML File :D

ومن الصبح شغال في عملية الـ parsing والـ persisting :D

شارفت علي الإنتهاء :)

صــفــر

#5

عصام باشا

هل يمكنني في أثناء عملية القراءة أن أقوم بكتابة بعض ال nodes?

تحياتي

حزمة المحرك الإصدارة 0.8

أي أحد يجد أني ظلمته فليراسلني

وبإذن الله لو كان له حق سيأخذه

728x90.png

#6

حسب معلومتي فإن DOM هي التي تسمح بإضافة Node إلى المستند، لأن ال SAX parser ليست لديه أي فكرة عن بنية المستند، لذلك لا يمكنه تغيير هذه البنية.

لست متأكدا من الموضوع، سأبحث قليلا لأرى إن كنت مخطئا

#7

شكرا يا شيخ عصام

سبـــــحــــان الله و بحمده سبحان الله العظيم

403100468.gif

#8

شيخ :blink: :lol:

#9
اقتباس
حسب معلومتي فإن DOM هي التي تسمح بإضافة Node إلى المستند، لأن ال SAX parser ليست لديه أي فكرة عن بنية المستند، لذلك لا يمكنه تغيير هذه البنية.

يمكنك استخدام ال StringBufffer او ال XML DOM او XMLWriter او ال SAX ايضاً

سواء استخدمت SAX او XML DOM فالاثنين ليس ليهما فكرة ببنية ملف ال XML إلا من خلال مضاهاته ب DTD او XDR او XSD و هو ما نسميه Validating XML

اقتباس

هل يمكنني في أثناء عملية القراءة أن أقوم بكتابة بعض ال nodes?

هل تقصد الكتابة بينما الملف فى الذاكرة ؟

تم تعديل هذه المشاركة بواسطة طارق إبراهيم في 13 يناير 2010 في 14:18

Technical Lead Developer

My LinkedIn Profile

اللهم قنى شر الجهل و الجهلاء

( اقْتَرَبَ لِلنَّاسِ حِسَابُهُمْ وَهُمْ فِي غَفْلَةٍ مَّعْرِضُونَ ) {الأنبياء:1}

#10

عندما قلت أن DOM يتعرف على بنية المستند، كنت أعني أنه عند استرجاع element ما، فإنه يمكننا معرفة الparent node وال child nodes، لأن DOM يبني شجرة تمثل المستند، وهذا شيء لا يوفره SAX. طبعا ممكن عمله يدويا، لكني أتكلم عن ما توفره كل API افتراضيا

طارق إبراهيم كتب:

هل تقصد الكتابة بينما الملف فى الذاكرة ؟

لا وجود للمستند في الذاكرة في حالة SAX، وهذا أساس الفرق بين SAX و DOM.

تم تعديل هذه المشاركة بواسطة herch في 13 يناير 2010 في 14:33

#11

معذرة ... قصدت عندما يكون الملف قيد الاستخدام

تم تعديل هذه المشاركة بواسطة طارق إبراهيم في 13 يناير 2010 في 15:25

Technical Lead Developer

My LinkedIn Profile

اللهم قنى شر الجهل و الجهلاء

( اقْتَرَبَ لِلنَّاسِ حِسَابُهُمْ وَهُمْ فِي غَفْلَةٍ مَّعْرِضُونَ ) {الأنبياء:1}

#12

هناك API تشبه SAX لكنها بخلافه تتيح الكتابة

StAX : Streaming API for XML

#13

موضوع مميّز كالعادة أخ herch .

ولكن لدي سؤال ،

لماذا استخدمت XMLReader ولم تستخدم SAXParser ؟

ما أراه أن XMLReader يقوم بالمهمة بشكل ممتاز .. بالتالي السؤال ،

مافائدة SAXParser ؟ ولماذا لم تستخدمه ؟

أنا قرأت عن العلاقة بين SAXParser و XMLReader ، وما فهمته أن SAXParser يعتمد في عمله ( implementation الخاص به ) على XMLReader ، ولكن لم أتوقع أنه يمكن استخدام XMLReader لوحده .

هذه الطريقة التي استخدم في قراءة ملف XML بطريقة SAX :

            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            try
            {
            SAXParser saxParser  = saxFactory.newSAXParser();
            saxParser.parse(new File("academic-report.xml"),new myDefaultHandler());
            }
            catch(Exception e)
            {

            }

ما رأيك ؟

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#14

SAX هو مشروع مستقل عن Sun، موقعهم هو http://www.saxproject.org

XMLReader تابعة لمشروع SAX، وهي interface تمثل ال parser الذي سيقوم بتصفح المستند XML. هي interface فقط، تعطي الهيكل العام لأي SAX parser، والفئة SAXParser هي implementation توفرها Sun لهاته ال interface

XMLReaderFactory.createXMLReader() تعيد instance من الفئة التي تعمل implentation ل XMLReader، هاته الفئة ليست بالضرورة SAXParser، ممكن تكون تستعمل implementation أخرى غير التي توفرها Sun، مثل GNU JAXP، Apache Xerces, Oracle XDK, Crimson. ويمكن تحديد ال parser عند تشغيل البرنامج بتحديد قيمة الخاصية org.xml.sax.driver

وبالتالي فاستعمال XMLReader يجعلك مستقلا عن ال implementation المستعملة، بينما استعمال SAXParser يجعلك مقيدا بال implementation الخاصة ب Sun.

للمزيد من التفاصيل --> http://docstore.mik.ua/orelly/xml/sax2/ch03_02.htm

تم تعديل هذه المشاركة بواسطة herch في 19 فبراير 2010 في 15:13

1
#15

شرح واضح ، والصورة واضحة الآن .

الله يجزاك خير .

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#16

كيف احصل على اسم ال Attribute و قيمةته يا سيد عصام ؟؟؟

سبـــــحــــان الله و بحمده سبحان الله العظيم

403100468.gif

#17

يمكن استرجاع ال attributes وقيمها داخل الطريقة startElement

void startElement(String uri, String localName, String qName, Attributes atts)

وذلك باستعمال المعطى atts من نوع Attributes. راجع ال documentation لتعرف كل الطرق المتوفرة، والتي تسمح بمعرفة قيمة ال attribute حسب اﻹسم أو ال index، وأيضا معرفة إسم ال attribute الموجود في index معين ....

#18

أخى الكريم - اشكرك على طرحك لمثل هذا الموضوع المهم. لكن كنت اود لو استفضت ووضحت ايضا Pull Parsing و Push Parsing فى كلامك عن Stax و Sax. بالاضافة الى التحدث عن مشاكل الفلترة للملفات الكبيرة سواء بـ XPath او XQuery.

DOM and SAX المقدمين من شركة صن من الاساليب التقليدية فى التعامل مع XML فى الجافا لكنهم غير مناسبين للاستخدام العملى فى كافة تطبيقاته خاصة فيما يتعلق بالفلترة و استخدام XPath حيث ستكون ملزم على استخدام DOM فى التعامل مع الفلترة و اكيد لايصلح استخدامه مع الملفات الكبيرة. و كما اشرت فانه يوجد اكثر من implementation لـ SAX2. لكن مع ذلك فتوجد بعد المشاكل فى فلترة الملفات الكبيرة و هى ممكن من اهم المواضيع لاستخدام ملفات XML.

TrAX و SAXON من المكاتب المعروفة و التى تطرقت لحل مشاكل البحث و الفلترة بالاعتماد على XSLT Rules بشكل عام.

أشكرك اخى على مثل هذا الموضوع و اتمنى لو تحدثت لاحقا عن الفلترة و كيفية تطبيق القواعد على ملفات XML.

#19

أستخدام XPath في الجافا

بالنسبة للباقي، ربما لاحقا، فلا أستطيع حاليا.

مواضيع مشابهة