السلام عليكم ورحمة الله وبركاته ..............
تتلخص عملية كتابة بيانات XML بواسطة الفئة XmlTextWriter بالخطوات التالية :
1 - تعريف متغير من نوع الفئة XmlTextWriter مع تمرير مسار واسم الملف الذي سوف تقوم الفئة بانشائه وكذا تمرير ترميز المحارف Encoding .
2 - كتابة البيانات بواسطة طرق هذه الفئة .
3 - اغلاق المجرى المرتبط بالملف المنشئ بواسطة الطريقة Close .
الان انظر الى مستند XML التالي :
<?xml version="1.0"?>
<!-- am comment -->
<Root>
<Element No="1">am string</Element>
<!--
<Element No="2">am string</Element>
<Element No="3">am string</Element>
<Element No="4">am string</Element>
-->
</Root>
نلاحظ ان هذا المستند يتكون من العناصر التاليه :
ال Tag الذي يدل على رقم اصدار ملف ال XML الحالي .
تعليق يحتوي على العبارة Am comment .
العنصر المسمى Root والذي يمثل العقدة الجذر للمستند .
العنصر ُElement والذي يحتوي على الخاصية ( attribute ) المسماة No والقيمة النصية am String .
الان من اجل كتابة المستند السابق وجب علينا معرفة الطرق التي توفرها الفئة XmlTextWriter لكتابة كل عنصر من العناصر السابقة الذكر .
والطرق هي كالاتي :
من اجل كتابة ال Tag الذي يدل على رقم اصدار ملف ال XML الحالي نستخدم الطريقة :
WriteStartDocument()
من اجل كتابة التعليقات في المستند نستخدم الطريقة :
WriteComment
من اجل كتابة عنصر ما نستخدم الطريقه :
WriteStartElement
من اجل كتابة خاصية ( attribute ) لعنصر ما فاننا نستخدم الطريقه :
WriteAttributeString
وذلك بتمرير كلا من اسم الخاصية وقيمتها .
من اجل كتابة قيمة للعنصر فاننا نستخدم الطريقة :
WriteString
وذلك بتمرير القيمة النصية المراد كتابتها .
الان انظر الى الشكل التالي :
من الشكل السابق نلاحظ ان العنصر Root يقع في المستوى الاول بالنسبة للمستند ككل , وان العنصر Element
يقع في المستوى الاول بالنسبة للعنصر Root لذا وجب علينا اغلاقة ان اردنا الكتابة في المستوى الاول بالنسبة
للعنصر Root والا فان الكتابة سوف تتم داخل العنصر ُElement .
من اجل اغلاق المستويات فاننا نستخدم الطريقة :
WriteEndElement()
ملاحظـــــــــــــــــــــة
-------------------------- كل الملفات سوف تنشى في المجلد Bin للمشروع الذي تعمل عليه .
الان سوف نقوم بكتابة المستند الذي تم نقاشه من سابق :
Dim MyXml As New Xml.XmlTextWriter(IO.Directory.GetCurrentDirectory & "\TestXml.xml", System.Text.Encoding.Unicode)
/// تعريف متغير من نوع الفئة XmlTextWriter مع تمرير كلا من مسار واسم الملف الذي ستقوم هذه الفئة بانشائه وكذا
ترميز المحارف Encoding :
Dim i As Integer MyXml.WriteStartDocument
()
//// الطريقة التي تحدد اصدار ملف XML الحالي
MyXml.WriteComment("am comment")///// كتابة تعليق
MyXml.WriteStartElement("Root")///// كتابة العقدة الجذر Root
For i = 1 To 4
MyXml.WriteStartElement("Element")///// كتابة العقدة Element
MyXml.WriteAttributeString("No", i.ToString)///// كتابة الخاصية No مع اعطائها قيمة المتغير I
MyXml.WriteString("am string")///// كتابة القيمة النصية التي سوف يحتويها العنصر Element
MyXml.WriteEndElement()
///// اغلاق العنصر Element وذلك حتى نتمكن من كتابة عنصر Element اخر في نفس مستوى العنصر Element
السابق - جرب ان لاتقوم باستخدام الطريقة WriteEndElement !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Next MyXml.WriteEndElement()
//////// اغلاق العنصر الجذر Root
MyXml.Close()
///////// اغلاق المجرى المرتبط مع الملف
الان دعنا ناخذ مثالا اخر ...........
هناك لعبة مشهورة الا وهي لعبة كتابة الارقام و هي تتمثل في ان اعطيك رقما ومن ثم انت عليك ان تقوم بكتابة
الارقام من واحد الى ذلك الرقم حيث اننا نكتب الرقم مع الاعداد التي تسبقه فاذا فرضنا انه لدينا الرقم 5 فان حل
اللعبة سيكون كالاتي :
الحل
--------
1
2 يسبقه العدد 1
3 يسبقه العدد 2 و العدد 1
4 يسبقه العدد 3 و العدد 2 و العدد 1
5 يسبقه العدد 4 العدد 3 و العدد 2 و العدد 1
حسنا اذا قلت لك خذ الرقم 5 ولكنني اريد النتيجة على شكل ملف XML , فان الذي سوف تفعله سيكون مشابها
للاتي :
Dim i, j As Integer
Dim MyXml As New Xml.XmlTextWriter("c:\WhoB4Me.xml", System.Text.Encoding.Unicode)
MyXml.WriteStartDocument()
MyXml.WriteStartElement("Root")
For i = 1 To 5
MyXml.WriteStartElement("ElementNo_" & i.ToString)//////// كتابة الرقم ودعنا نتفترض اننا قد كتبنا العددين 1 و 2 واننا نقوم بكتابة العدد 3
For j = i - 1 To 1 Step -1
MyXml.WriteStartElement("Am_B4_U")////// كتابة الارقام التي تسبق المتغير I ان وجدت فان كان المتغير I يساوي 3 مثلا فانه عن طريق هذه الحلقة
سوف يتم كتابة عنصرين حيث يحتوي الاول على القيمة 1 والثاني على القيمة 2 وذلك باستخدام الطريقة WriteString
مع تمرير قيمة المتغير J لها انظر السطر التالي :
MyXml.WriteString(j.ToString)
///// كتابة قيمة المتغير J
MyXml.WriteEndElement()
/////// اغلاق العدد السابق من اجل كتابة عدد جديد ان وجد على مستوى العقدة التي تحتوي على القيمة 3
Next MyXml.WriteEndElement()
////////// اغلاق العقدة التي تحتوي على القيمة 3حتى نتمكن من كتابة عقدة جديدة تحتوي على الرقم 4 وهكذا .
Next MyXml.WriteEndElement()
/////// اغلاق العنصر الجذر Root
MyXml.Close()
///////// اغلاق المجرى المرتبط مع الملف
الان دعنا نستفيد من هذه الفئة بشكل عملي :
اذا افترضنا انه لدينا قاعدة بيانات اسمها Students Registeration وبها جدول اسمه Students حيث يحتوي هذا الجدول
على حقلين هما :
StudentNo
StudentName
كل ما سنقوم به الان هو سحب البيانات الموجودة داخل هذا الجدول الى داخل DataReader ومن ثم سنقوم باخذ
البيانات الموجودة في ال DataReader ومن ثم كتابتها الى ملف XML يقوم بوصف البيانات الموجودة داخل الجدول
Students .
اي اننا نريد مستند على هذه الهيئة :
<?xml version="1.0" encoding="utf-16" ?>
<Students_Registeration_DataBase>
<Tables>
<Student>
<Row>
<StudentNo>1</StudentNo>
<StudentName>Dr.Pain</StudentName>
</Row>
<Row>
<StudentNo>2</StudentNo>
<StudentName>gasan</StudentName>
</Row>
</Student>
</Tables>
</Students_Registeration_DataBase>
وهذا يعني انه لدينا قاعدة بيانات اسمها Students Registeration تحتوي على الجدول Students وبهذا الجدول
اثنان من الصفوف .
لكن قبل ذلك دعني اعرفك على بعض خصائص وطرق ال DataReader :
الخاصية FieldCount :
------------------------
تقوم هذه الخاصية بارجاع عدد الاعمدة الموجودة في الصف ( Row ) , وفي حالتنا هذه سوف تقوم بارجاع العدد 2
الخاصية Item :
-------------------
تقوم بارجاع قيمة العمود المحدد اي اننا لو قمنا بشئ مماثل للاتي :
Msgbox (dr.Item(0)) Msgbox (dr.Item(1))
وكان الحقل StudentNo يساوي 1 والحقل StudentName يساوي Dr.Pain فانها سوف ترجع :
1 و Dr.pain
او اذا قمنا بعمل التالي فاننا سوف نحصل على نفس النتيجة :
Msgbox (dr.Item("StudentNo"))
Msgbox (dr.Item("StudentName"))
الطريقة GetName :
-----------------------
تقوم بارجاع اسم العمود المحدد اي اننا لو قمنا بشئ مماثل للاتي :
Msgbox (dr.GetName(0)) Msgbox (dr.GetName(1))
فاننا سوف نحصل على :
StudentNo
StudentName
اي اسماء الاعمدة وليست قيمها .
الطريقة Read :
-----------------
تقوم بارجاع القيمة True طالما ان هناك صفوف للقراءة والعكس .
حسنا الان دعنا نصيغ الموضوع برمجيا :
Dim Cnn As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & IO.Directory.GetCurrentDirectory & "\Students Registeration.mdb")//////// تعريف كائن اتصال من النوع OleDbConnection
Dim Com As New OleDb.OleDbCommand("Select * from students", Cnn)////////// تعريف كائن امر من النوع OleDbCommand
Dim dr As OleDb.OleDbDataReader
//////// تعريف كائن من النوع OleDbDataReader
Cnn.Open()
//////// فتح كائن الاتصال
dr = Com.ExecuteReader
تعبئة الكائن dr بالصفوف بواسطة الطريقة ExecuteReader والتي تعيد فئة من نوع OleDbDataReader
والتي ينفذها الكائن OleDbCommand
Dim Writer As New Xml.XmlTextWriter(IO.Directory.GetCurrentDirectory & "\Students Table info.xml", System.Text.Encoding.Unicode)
///////// تعريف كائن من الفئة XmlTextWriter
Writer.WriteStartDocument()
//// الطريقة التي تحدد اصدار ملف XML الحالي
Writer.WriteStartElement("Students_Registeration_DataBase")/////// انشاء العقدة الجذر للمستند و المسماة Students_Registeration_DataBase
Writer.WriteStartElement("Tables")/////// انشاء العقدة المسماة جداول
Writer.WriteStartElement("Student")/////// انشاء العقدة التي تدل على ان هذه هي البيانات الخاصة بالجدول Student
Dim i As Integer While dr.Read
////////////// تنفيذ الطريقة Read والتي تعود بالقيمة True طالما ان هناك صفوف للقراءة
Writer.WriteStartElement("Row")/////////// انشاء عقدة صف جديد
For i = 0 To dr.FieldCount - 1
الدوران على بمقدار يساوي عدد الاعمدة الموجودة في الصف وذلك باستخدام الخاصية FieldCount
Writer.WriteStartElement(dr.GetName(i))
//////// انشاء عقدة جديدة تحتوي على اسم العمود وذلك باستخدام الطريقة GetName والتي تعود باسم العمود
Writer.WriteString(dr.Item(i))
//////// كتابة قيمة العمود وذلك باستخدام الخاصية Item والتي تعود بقيمة العمود
Writer.WriteEndElement()
////////// اغلاق العقدة التي تحتوي على اسم العمود
Next Writer.WriteEndElement()
////////// اغلاق العقدة المسماة Row اي اغلاق العقدة التي تشير الى صف ما والبدء بكتابة صف جديد
End While Writer.WriteEndElement()
////////// اغلاق العقدة Students
Writer.WriteEndElement()
////////// اغلاق العقدة Tables
Writer.WriteEndElement()
/////// اغلاق العقدة Students_Registeration_DataBase
Writer.Close()
/////// اغلاق مجرى الملف
Cnn.Close()
///////// اغلاق كائن الاتصال
الكلام السابق قد يراه البعض جميلا لكن دعني اسالك الان ماذا لو اردنا كتابة بيانات موجودة في DataReader اخرى
اعتقد ان الاجابة ستكون اننا سوف نقوم بكتابة الكود السابق مرة اخرى مع بعض التعديلات في الاسماء !!!!!!!!!
لذلك لماذا لا نستفيد من تقنية الوراثة ونقوم بعمل فئة ترث الفئة XmlTextWriter ومن ثم نقوم بتضمين طريقة
اسمها WriteDataReader , حيث اننا سوف نقوم بتمرير فئة من نوع DataReader للطريقة ومن ثم سوف تتولى هي
مهمة كتابة البيانات الموجودة في ال DataReader .
اذن دعنا نقوم بعمل فئة اسمها MyXmlTextWriter ومن ثم دعها ترث من الفئة XmlTextWriter .
لكن لا تنسى بعد الوراثة ان تقوم بتنفيذ طريقة البناء الخاصة بالفئة الجديدة MyXmlTextWriter .
Class MyXmlTextWriter Inherits Xml.XmlTextWriter Sub New(ByVal filename As String, ByVal encoding As System.Text.Encoding) MyBase.New(filename, encoding) End Sub
الان قم ببناء الطريقة التي اسمها WriteDataReader :
Sub WriteDataReader(ByVal dr As OleDbDataReader)
Dim i As Integer
While dr.Read
WriteStartElement("Row")
For i = 0 To dr.FieldCount - 1
WriteStartElement(dr.GetName(i))
WriteString(dr.Item(i))
WriteEndElement()
Next
WriteEndElement()
End While
End Subلاحظ ان الكود السابق لم يتغير عن الكود المكتوب قبل الوراثة لكتابة بيانات ال DataReader لذا لا داعي للتعليق
عليه .
الان ما عليك الا ان تقوم باستدعاء الطريقة WriteDataReader مع تمرير فئة من نوع DataReader .
Writer.WriteDataReader(dr)
الان افتح المستند Students Table info_After والموجود في المجلد Bin الخاص بالمشروح .
ماذ تلاحظ ؟؟؟؟؟ الملاحظ ان الصفوف فصلت بواسطة الكلمة Row حسنا ان لم تعجبك هذه الكلمة واردت ان
تعطي لمستجدم الفئة حرية اكبر في اختيار الاسم الذي سوف يتم فصل الصفوف به ما عليك الا ان تقوم باعادة
تحميل الطريقة WriteDataReader مع جعل الطريقة المحمله تستقبل وسيطا نصيا الى جانب وسيط الفئة DateReader
Overloads Sub WriteDataReader(ByVal dr As OleDbDataReader, ByVal SplitterName As String) Dim i As Integer While dr.Read WriteStartElement(SplitterName) For i = 0 To dr.FieldCount - 1 WriteStartElement(dr.GetName(i)) WriteString(dr.Item(i)) WriteEndElement() Next WriteEndElement() End While End Sub
الان بقى لي تعليق اخير على الطريقة WriteDataReader نلاحظ ان الوسيط الاول عبارة عن فئة من النوع OleDbDataReader
الان دعني اسالك سؤالا كم انواع DataReader لدينا ؟؟؟؟؟؟؟؟؟؟؟
لدينا خمسة انواع وهي :
OdbcDataReader
OleDbDataReader
OracleDataReader
SqlCeDataReader
SqlDataReader
لاحظ واحدة من اجل قاعدة بيانات اكسيس والثانية لي SqlServer وواحدة لي Oracle ..... الخ .
اذن اذا كانت معي DataReader من نوع SqlDataReader والخاصة بي SqlServer فانه للاسف لن تعمل معي الطريقة
WriteDataReader بنجاح لان نوع الوسيط كان من نوع OleDbDataReader !! اذن ما العمل ؟؟؟؟؟؟؟
قد يقول احد ما الحل هو في اعادة تحميل الطريقة WriteDataReader خمس مرات مع تغيير نوع الوسيط في كل مرة :
Overloads Sub WriteDataReader(ByVal dr As OdbcDataReader) End Sub Overloads Sub WriteDataReader(ByVal dr As OleDbDataReader ) End Sub Overloads Sub WriteDataReader(ByVal dr As OracleDataReader) End Sub Overloads Sub WriteDataReader(ByVal dr As SqlCeDataReader) End Sub Overloads Sub WriteDataReader(ByVal dr As SqlDataReader ) End Sub
ومن ثم اعادة تحميل الطرق التي تحتويعلى اسم فاصل الصفوف :
Overloads Sub WriteDataReader(ByVal dr As OdbcDataReader, ByVal SplitterName As String) End Sub Overloads Sub WriteDataReader(ByVal dr As OleDbDataReader , ByVal SplitterName As String) End Sub Overloads Sub WriteDataReader(ByVal dr As OracleDataReader , ByVal SplitterName As String) End Sub Overloads Sub WriteDataReader(ByVal dr As SqlCeDataReader , ByVal SplitterName As String) End Sub Overloads Sub WriteDataReader(ByVal dr As SqlDataReader , ByVal SplitterName As String) End Sub
اذن لاحظ معي اننا قمنا بتكرار العمل السابق لعشر مرات وهذا قد يبدوا مقبولا لكن دعني اسالك سؤالا جديدا
اذا افترضنا ان هناك 100 DataReader جديدة ظهرت في السوق فهل ستقوم باعادة تحميل الطرق ؟؟؟؟؟ ام ماذا ؟؟
اذا كان الجواب نعم فلاحظ انك سوف تقوم باعادة تحميل الطريقة WriteDataReader لي 200 مرة .
ولكن ان كنت ذكيا فانك سوف تقول لي لا لن اقوم باعادة تحميل الطرق !!!!!!!! بل ساقوم باستخدام الواجهات حيث
انني سوف اقوم بتحميل الطريقة لمرتين فقط ولكنني لن امرر وسيطا من النوع OleDbDataReader او من النوع
SqlDataReader او كائن من كان من انواع ال DataReader بل ساقوم بتمرير واجهة !!!!! نعم واجهة وهذه الواجهة هي
IDataReader والتي تنفذها كل ال DataReader بدون استثناء :
Overloads Sub WriteDataReader(ByVal dr As IDataReader) code ........... End Sub Overloads Sub WriteDataReader(ByVal dr As IDataReader, ByVal SplitterName As String) code ........... End Sub
من خلال هذه الواجهة تضمن الطريقة WriteDataReader ان كل الفئات التي سوف تمرر لها تفوم بتنفيذ اعضاء الواجهة
IDataReader .
ان لم تقتنع بفكرة تمرير الواجهة IDataReader اقول لك دعني انا وانت نستعرض اعضاء الواجهة IDataReader :
من الاعضاء السابقيين نلاحظ ان الطريقة Read هي عضو من اعضاء الواجهة IDataReader .
تقول لي الان ماذا عن بقية الخصائص والطرق والتي هي :
FieldCount
GetName
Item
ستقول لي انها ليست اعضاء في الواجهة IDataReader !!!! لذا اقول لك انظر الى الشكل التالي :
نلاحظ من الشكل السابق ان الواجهة IDataReader ترث اعضاء الواجهة IDataRecord وايضا يرينا الشكل السابق
الفئات التي تقوم بتنفيذ الواجهة IDataReader .
الان انظر الى الشكل التالي :
من الشكل السابق نلاحظ ان كل من :
FieldCount
GetName
Item
اعضاء في الفئة IDataRecord وبالتالي تلقائيا يصبحون اعضاء في الواجهة IDataReader لانها ترث من الواجهة IDataRecord
وبما ان :
FieldCount
GetName
Item
Read
اعضاء في الواجهة IDataReader وبما ان الفئات :
OdbcDataReader
OleDbDataReader
OracleDataReader
SqlCeDataReader
SqlDataReader
تقوم بتنفيذ الواجهة IDataReader اذن بالتالي كل فئة من الفئات السابقة سوف تحتوي على الطرق والخصائص الموجودة
في الواجهة IDataReader ومن ضمنها :
FieldCount
GetName
Item
Read
اذن لن تهتم الطريقة WriteDataReader بنوع الوسيط الممرر بعد الان شريطة ان ينفذ الواجهة IDataReader .
والسلام عليكم ورحمة الله وبركاته ........................................................................
اخوكم Dr.Pain

