اذهب إلى المحتوى

مقدمة مختصرة للغة XML واستعمالها في تطبيقات جافا


رضوى العربي

عند تخزين بياناتٍ معينة بملفٍ معين أو نقلها خلال شبكةٍ معينة، سيكون من الضروري تمثيلها بطريقةٍ ما بشرط أن تَسمَح تلك الطريقة بإعادة بناء البيانات لاحقًا عند قراءة الملف أو عند تَسلُّم البيانات. رأينا بالفعل أسبابًا جيدةً تدفعنا لتفضيل التمثيلات النصية المُعتمِدة على المحارف، ومع ذلك هناك طرائقٌ كثيرةٌ لتمثيل مجموعةٍ معينةٍ من البيانات تمثيلًا نصيًا. سنُقدِّم بهذا المقال لمحةً مختصرةً عن أحد أكثر أنواع تمثيل البيانات data representation شيوعًا.

تُعدّ لغة الترميز القابلة للامتداد eXtensible Markup Language -أو اختصارًا XML- أسلوبًا لإنشاء لغاتٍ لتمثيل البيانات. هناك جانبان أو مستويان بلغة XML؛ حيث تُخصِّص XML في المستوى الأول قواعد صيغة صارمةً ولكنها بسيطةً نوعًا ما، وتُعدّ أي متتالية محارف تَتبِع تلك القواعد مستند XML سليم؛ بينما تُوفِّر XML على المستوى الآخر طريقةً لإضافة المزيد من القيود على ما يُمكِن كتابته بالمستند من خلال ربط مستند XML معين بما يُعرَف باسم "تعريف نوع المستند Document Type Definition" -أو اختصارًا DTD- الذي يُخصِّص قائمةً بالأشياء المسموح بكتابتها بمستند XML.

يُقال على مستندات XML السليمة المُرتبِطَة بتعريف نوع مستند معين والمُتبعِّة للقواعد التي خصَّصها ذلك التعريف "مستندات XML صالحة". يُمكِننا النظر إلى لغة XML كما لو كانت صيغةً عامةً لتمثيل البيانات؛ في حين تُخصِّص DTD الكيفية التي ينبغي بها استخدام XML لتمثيل نوعٍ معينٍ من البيانات. تتوفَّر بدائلٌ أخرى بخلاف DTD، مثل XML schemas لتعريف مستندات XML صالحة، ولكننا لن نناقشها هنا.

لا يوجد شيءٌ سحريٌ بخصوص XML، فهي ليست مثالية، وتُعدُّ في الحقيقة لغةً مُسهبةً verbose ويراها البعض قبيحة، ولكنها من الناحية الأخرى مرنةٌ للغاية؛ حيث يُمكِن اِستخدَامها لتمثيل أي نوعٍ من البيانات تقريبًا. لقد صُمِّمت من البداية لتدعم جميع لغات البرمجة، والأهم من ذلك، أنها قد أصبحت بالفعل معيارًا مقبولًا على نطاقٍ واسع، فبإمكان جميع لغات البرمجة معالجة مستندات XML، بل ويتوفَّر تعريف نوع مستند DTD قياسي لوصف مختلف أنواع البيانات.

هناك طرائقٌ مختلفةٌ كثيرة لتصميم لغةٍ لتمثيل مجموعةٍ من البيانات، ولكن حظت XML بشعبيةٍ واسعة، بل إنها حتى قد وجدت طريقها إلى جميع تقنيات المعلومات. على سبيل المثال، هناك لغات XML لتمثيل كُلٍ من التعبيرات الحسابية "MathML" والرموز الموسيقية "MusicXML" والجزيئات والتفاعلات الكيميائية "CML" ورسومات الفيكتور "SVG" وغيرها من المعلومات.

بالإضافة إلى ذلك، تَستخِدم برامج OpenOffice والإصدارات الأحدث من Microsoft Office لغة XML بصيغ المستندات لتطبيقات معالجة النصوص وجداول البيانات spreadsheets والعروض. مثالٌ آخر هو لغة مشاركة المواقع "RSS, ATOM"، التي سهَّلت على المواقع الإلكترونية والمدونات والجرائد الإخبارية إنشاء قائمةٍ بالعناوين الأحدث المتاحة بصيغةٍ قياسيةٍ قابلةٍ للِاستخدَام بواسطة مواقع ومتصفحات الويب، وهي في الواقع نفس الصيغة المُستخدَمة لنشر المدونات الصوتية podcasts. تُعدّ XML عمومًا الصيغة الأكثر شيوعًا لتبادل المعلومات إلكترونيًا.

ليس الهدف هنا هو إطلاعك على كل شيء يُمكِن معرفته عن XML، وإنما سنشرح فقط طرائقًا قليلةً يَصلُح معها استخدام XML بالبرامج؛ أي أننا لن نناقش أي شيءٍ آخر عن تعريفات نوع المستند DTD، أو مستندات XML الصالحة، وإنما سنَكتفِي باستخدام مستندات XML سليمة دون ربطها بأي تعريفاتٍ لنوع المستند DTD؛ فهي عادةً ما تكون كافيةً للعديد من الأغراض.

قواعد صيغة بسيطة للغة XML

إذا كنت تعرف لغة HTML -اللغة المُستخدَمة لكتابة صفحات الإنترنت-، فستبدو لغة XML مألوفةً بالنسبة لك، حيث تشبه مستندات XML كثيرًا مستندات HTML. لا تُعدّ لغة HTML هي نفسها لغة XML؛ فهي لا تَتبِع جميع قواعد الصيغة الصارمة الخاصة بلغة XML، ولكن الأفكار الأساسية هي نفسها. نعرض فيما يلي مثالًا قصيرًا على مستند XML سليم:

<?xml version="1.0"?>
<simplepaint version="1.0">
   <background red='1' green='0.6' blue='0.2'/>
   <curve>
      <color red='0' green='0' blue='1'/>
      <symmetric>false</symmetric>
      <point x='83' y='96'/>
      <point x='116' y='149'/>
      <point x='159' y='215'/>
      <point x='216' y='294'/>
      <point x='264' y='359'/>
      <point x='309' y='418'/>
      <point x='371' y='499'/>
      <point x='400' y='543'/>
   </curve>
   <curve>
      <color red='1' green='1' blue='1'/>
      <symmetric>true</symmetric>
      <point x='54' y='305'/>
      <point x='79' y='289'/>
      <point x='128' y='262'/>
      <point x='190' y='236'/>
      <point x='253' y='209'/>
      <point x='341' y='158'/>
   </curve>
</simplepaint>

يُعدّ السطر الأول بالأعلى اختياريًا، حيث يُشير ببساطةٍ إلى أن ما يليه هو مستند XML. قد يتضمَّن هذا السطر معلوماتٍ أخرى أيضًا، مثل الترميز المُستخدَم لترميز محارف المستند إلى صيغةٍ ثنائية. وقد يتضمَّن المستند سطرًا للموجه "DOCTYPE" لتحديد تعريف نوع المستند.

يتكوَّن المستند بخلاف السطر الأول من مجموعةٍ من العناصر elements والسمات attributes والمحتويات النصية. يبدأ أي عنصرٍ بوسم tag، مثل <curve> وينتهي بوسم الغلق المقابل، مثل </curve>، ونجد محتويات العنصر بين هذين الوسمين، والتي يُمكِن أن تتكوَّن من نصٍ وعناصرٍ متداخلة أخرى.

يُعدّ المحتوى النصي الوحيد بالمثال السابق هو القيمة true أو false بالعنصر <symmetric>. إذا لم يحتوِ عنصرٌ معينٌ على أي شيء، يُمكِننا دمج وسميه للفتح والغلق بوسمٍ واحدٍ فارغ بكتابة الاختصار ‎<point x='83' y='96'/>‎ بدلًا من <point x='83' y='96'></point>. لاحِظ استخدام "/" قبل "<". قد يتضمَّن الوسم عِدّة سمات، مثل x و y بالوسم ‎<point x='83' y='96'/>‎، و version بالوسم <simplepaint version="1.0">. قد يتضمَّن المستند أشياءً قليلةً أخرى، مثل التعليقات ولكننا لن نناقشها هنا.

ينبغي لأي مؤلف مستند XML سليم أن يختار أسماء الوسوم والسمات، وبطبيعة الحال عليه اختيار أسماءٍ ذات معنًى مفهوم للقارئ. في المقابل، إذا كان المستند يعتمد على تعريف نوع مستند DTD معين، فإن مؤلف التعريف هو من يختار أسماء الوسوم.

يجب على أي مستند XML سليم أن يَتبِّع مجموعةً صارمةً من قواعد الصياغة syntax، نُلخِّص أهمها فيما يلي:

  • تُميِّز أسماء الوسوم والسمات بمستندات XML بين حالة الأحرف case sensitive؛ فقد تتكوَّن الأسماء من مجموعةٍ من الأحرف والأرقام وبعض المحارف الأخرى، ولكن لا بُدّ أن تبدأ بحرف.
  • ليس للفراغات ومحرف نهاية السطر أي أهمية إلا إذا وقعت بالمحتويات النصية؛ فعند كتابة وسمٍ معينٍ، ولم يَكُن هذا الوسم فارغًا، فيجب إذًا أن يُقابِله وسم غلق؛ ويَعنِي ذلك أنه إذا تداخلت عدة عناصرٍ مع بعضها، فيجب أن يكون التداخل سليمًا. بتعبيرٍ آخر، إذا تضمَّن عنصرٌ معينٌ وسمًا، فيجب أن يَقع وسم غلقه ضمن نفس العنصر.
  • لا بُدّ أن يحتوي أي مستندٍ على عنصر جذر root، والذي يحتوي بدوره على جميع العناصر الأخرى. لاحِظ أن اسم الوسم المُمثِّل لعنصر الجذر بالمثال السابق، هو simplepaint.
  • لا بُدّ أن يكون لأي سمةٍ قيمة، والتي ينبغي أن تكون محاطةً بعلامتي اقتباس إما مفردة أو مزدوجة.
  • في حالة اِستخدَام المحارف الخاصة > و & بقيمةٍ لسمة أو بمحتوى نصي، فيجب أن تُكْتَب على النحو التالي ‎&gt;‎ و ‎&quot;‎. أمثلة أخرى على ذلك، هي ‎&gt;‎ و ‎&quot;‎ و ‎&apos;‎، والتي تُمثِّل > وعلامة اقتباسٍ مزدوجة وعلامة اقتباسٍ مفردة على الترتيب. يُمكِن تعريف المزيد بتعريف DTD.

يُعدّ ما سَبَق مقدمةً سريعةً عن مستندات XML، والتي ربما لن تساعدك على فهم كل شيء تقابله بمستند XML، ولكنها ستُمكِّنك على الأقل من تصميم مستندات XML سليمة لتمثيل بعض بنى البيانات data structures المُستخدَمة ببرامج جافا.

العمل مع شجرة DOM

صمَّمنا في الأعلى مثال XML لتخزين معلوماتٍ عن رسومٍ بسيطة يَرسِمها المُستخدِم، واعتمدنا على البرنامج التوضيحي SimplePaint2.java من مقال مفهوم المصفوفات الديناميكية (ArrayLists) في جافا فيما هو مُتعلِّقٌ برسم تلك الرسوم. سنناقش بهذا المقال نسخةً أخرى من نفس البرنامج، والتي يُمكِنها حفظ رسوم المُستخدِم بملف بياناتٍ معتمِدٍ على صيغة XML. يُمكِنك الإطلاع على شيفرة النسخة الجديدة من SimplePaintWithXML.java.

سنَستخدِم مستند XML التوضيحي الذي عرضناه بالأعلى ضمن هذا البرنامج. في الحقيقة، لقد صمَّمنا هذا المستند بحيث يحتوي على جميع البيانات المطلوبة لإعادة إنشاء الكائن المُمثِّل للرسمة، والذي ينتمي للصنف SimplePaint؛ حيث يتضمَّن المستند على سبيل المثال معلوماتٍ عن لون خلفية الصورة وكذلك قائمةً بالمنحنيات التي رَسَمها المُستخدِم. لاحِظ احتواء كل عنصر <curve> على بيانات كائنٍ من النوع CurveData.

من السهل كتابة برنامج يُخرِج البيانات بصيغة XML، مع الحرص طبعًا على اتباع جميع قواعد صيغة XML، حيث تبيّن الشيفرة التالية الطريقة التي يَستخدِمها الصنف SimplePaintWithXML لإرسال بيانات رسمةٍ من النوع SimplePaint إلى out من النوع PrintWriter. يَنتُج عن ذلك مستند XML بنفس هيئة المثال المُوضَح بالأعلى:

out.println("<?xml version=\"1.0\"?>");
out.println("<simplepaint version=\"1.0\">");
out.println("   <background red='" + backgroundColor.getRed() + "' green='" +
        backgroundColor.getGreen() + "' blue='" + backgroundColor.getBlue() + "'/>");
for (CurveData c : curves) {
    out.println("   <curve>");
    out.println("      <color red='" + c.color.getRed() + "' green='" +
            c.color.getGreen() + "' blue='" + c.color.getBlue() + "'/>");
    out.println("      <symmetric>" + c.symmetric + "</symmetric>");
    for (Point2D pt : c.points)
        out.println("      <point x='" + pt.getX() + "' y='" + pt.getY() + "'/>");
    out.println("   </curve>");
}
out.println("</simplepaint>");

قراءة البيانات من مستند XML إلى البرنامج مرةً أخرى هي عمليةٌ أخرى تمامًا، حيث ينبغي تحليل parse المستند واستخراج البيانات منه؛ لنتمكَّن من إعادة إنشاء بنية البيانات المُمثِلة للمستند، وهو ما يتطلّب الكثير من العمل. تُوفِّر جافا لحسن الحظ واجهة برمجة تطبيقات قياسية standard API لتحليل مستندات XML ومعالجتها، وهي تُوفِّر في الواقع واجهتين ولكننا سنتعامل مع واحدةٍ منهما فقط.

يتكوَّن أي مستند XML سليم من بنيةٍ معروفةٍ مكوَّنة من مجموعةٍ من العناصر elements مُتضمَّنةً مجموعةً من السمات attributes؛ والعناصر المتداخلة؛ والمحتويات النصية. يُمكِننا إنشاء بنية بياناتٍ مكافئةً لبنية المستند ومحتوياته في ذاكرة الحاسوب، حيث تتوفَّر الكثير من الطرائق لذلك، ولكن يُعدّ تمثيل نموذج كائن المستند Document Object Model -أو اختصارًا DOM- أكثرها شيوعًا؛ حيث يُخصِّص هذا النموذج طريقةً لبناء بنية بياناتٍ مكافئة لمستندات XML؛ كما أنه يُعرِّف مجموعةً من التوابع methods القياسية، التي تَسمَح باسترجاع البيانات الموجودة ضمن تلك البنية.

تُعدّ تلك البنية أشبه ما تكون بشجرةٍ تُكافئ بنيتها بنية المستند، حيث تتكوَّن الشجرة من أنواعٍ مختلفة من العُقد nodes المُستخدَمة لتمثيل العناصر والسمات والنصوص، كما قد تحتوي على أنواعٍ آخرى من العُقد لتمثيل جوانبٍ أخرى من XML. سنُركز فقط على العقد المُمثِّلة للعناصر بسبب عدم القدرة على معالجة السمات والنصوص مباشرةً دون معالجة ما يُقابِلها من عُقد.

يُمكِّنك البرنامج التوضيحي XMLDemo.java من التعامل مع كُلٍ من XML و DOM. يتضمَّن البرنامج صندوقًا نصيًا حيث يُمكِنك كتابة مستند XML، كما يحتوي على مستند XML توضيحي من هذا المقال افتراضيًا. إذا ضغطت على الزر "Parse XML Input"، سيقرأ البرنامج المستند المكتوب بالصندوق، وسيحاول بناء تمثيل DOM له. إذا لم تَكْن المُدْخلات مُمثِّلةً لمستند XML سليم، فستظهر رسالة خطأ؛ أما إذا كانت سليمة، فسيجتاز البرنامج تمثيل DOM بالكامل، وسيَعرِض قائمةً بكل العناصر والسمات والمحتويات النصية التي سيُقابِلها. يَستخدِم البرنامج بعض التقنيات لمعالجة XML، ولكننا لن نناقشها هنا.

يُمكِننا إنشاء تمثيل DOM لمستند XML باستخدام التعليمتين التاليتين. إذا كان selectedFile متغيرًا من النوع File يُمثِّل مستند XML، فسيكون xmldoc من النوع Document:

DocumentBuilder docReader 
                 = DocumentBuilderFactory.newInstance().newDocumentBuilder();
xmldoc = docReader.parse(selectedFile);

تَفتَح الشيفرة السابقة الملف، وتقرأ محتوياته، ثم تَبنِي تمثيل DOM. ستَجِد الصنفين DocumentBuilder و DocumentBuilderFactory بحزمة javax.xml.parsers. يُحاوِل التابع docReader.parse()‎ إجراء العملية، ويُبلِّغ عن اعتراضٍ إذا لم يتمكَّن من قراءة الملف أو إذا لم يحتوِ الملف على مستند XML صالح. في المقابل، إذا نجح التابع، فإنه يعيد كائنًا يُمثِّل كامل المستند. تُعدّ هذه العملية معقدةً بالتأكيد، ولكنها بُرمجَت بالكامل مرةً واحدةً، وضُمِّنَت بتابعٍ يُمكِنك اِستخدَامه بسهولة بأي برنامج جافا، وهو ما يُمثِّل إحدى فوائد الاعتماد على صيغةٍ قياسية.

يُمكِنك الإطلاع على تعريف بنية البيانات DOM بحزمة org.w3c.dom، والتي تحتوي على أنواع بياناتٍ مختلفة لتمثيل مستند XML بالكامل؛ وكذلك لتمثيل العقد الموجودة بالمستند على حدى. يشير الاسم "org.w3c" إلى اتحاد شبكة الويب العالمية World Wide Web Consortium -أو اختصارًا W3C، والمسؤولة عن تنظيم المعايير القياسية لتقنيات الويب.

يُعدّ تمثيل DOM مثل XML معيارًا عامًا؛ أي لا يختص بلغة جافا دون غيرها. سنحتاج بهذا المثال أنواع البيانات التالية Document و Node و Element و NodeList، والمُعرَّفة جميعًا على أنها واجهات interfaces لا أصناف، ولكن هذا غير مهم. سنَستخدِم التوابع المُعرَّفة بتلك الأنواع للوصول إلى البيانات الموجودة بتمثيل DOM لمستند XML.

يعيد التابع docReader.parse()‎ قيمةً من النوع Document، والتي كانت xmldoc في المثال السابق، وهي تُمثِّل مستند XML بالكامل. سنحتاج إلى التابع التالي فقط من هذا الصنف. إذا كان xmldoc من النوع Document، يُمكِننا كتابة ما يلي:

xmldoc.getDocumentElement()

يُعيد هذا التابع قيمةً من النوع Element تُمثِّل عنصر الجذر root الخاص بالمستند؛ وهوالعنصر الموجود بأعلى مستوى بالمستند كما ذكرنا مُسبقًا، ويتضمَّن كافة العناصر الأخرى. يتكوَّن عنصر الجذر بمستند XML الخاص بالمثال السابق من هذا المقال من الوسمين <simplepaint version="1.0"‎> و </simplepaint>، وكل ما هو موجودٌ بينهما. تُمثَّل العناصر الواقعة داخل عنصر الجذر باستخدام عقد nodes، والتي تُعدّ بمثابة أبناء عقدة الجذر. تتضمَّن كائنات النوع Element مجموعةً من التوابع المفيدة، والتي سنستعرِض بعضًا منها فيما يلي. إذا كان element من النوع Element، يُمكِننا استخدام التوابع التالية:

  • element.getTagName()‎: يُعيد سلسلةً نصيةً من النوع String للاسم المُستخدَم بالوسم الخاص بالعنصر؛ حيث سيُعيد على سبيل المثال السلسلة النصية "curve" للعنصر <curve>.
  • element.getAttribute(attrName)‎: إذا كان attrName اسم سمةٍ في العنصر، سيُعيد التابع قيمة تلك السمة. على سبيل المثال، يعيد الاستدعاء element.getAttribute("x")‎ السلسلة النصية "83" للعنصر <point x="83" y="42"/‎>، وتكون القيمة المعادة من النوع String دائمًا حتى لو كانت السمة تُمثِّل قيمة عددية. إذا لم يحتوِ عنصرٌ معين على سمةٍ للاسم المُخصَّص، فسيعيد التابع سلسلةً نصيةً فارغة.
  • element.getTextContent()‎: يُعيد سلسلةً نصيةً من النوع String تحتوي على المحتوى النصي الموجود بالعنصر، بما في ذلك محتوى العناصر الأخرى الواقعة ضمن العنصر.
  • element.getChildNodes()‎: يعيد قيمةً من النوع NodeList تحتوي على جميع العُقد الأبناء لذلك العنصر. تتضمَّن القائمة العقد المُمثِلة لأي عنصرٍ آخر أو محتوى نصي (إلى جانب أنواع أخرى من العقد) مُتداخِل مباشرةً مع العنصر. يَسمَح التابع getChildNodes()‎ باجتياز بنية البيانات DOM بالكامل بدءًا من عنصر الجذر، مرورًا بأبنائه، وأبناء أبنائه، وهكذا. يتوفَّر تابعٌ آخر يعيد جميع سمات العنصر، ولكننا لن نَستخدِمه هنا.
  • element.getElementsByTagName(tagName)‎: يُعيد قيمةً من النوع NodeList تحتوي على جميع العُقد المُمثِّلة لعناصر واقعةٍ داخل element، والتي لها نفس اسم الوسم المُخصَّص. يتضمَّن ذلك العناصر الواقعة بأي مستوى داخل element، وليس فقط تلك المتصلة مباشرةً معه. يَسمَح التابع getElementsByTagName()‎ بالوصول إلى بيانات مُحدَّدة ضمن المستند.

يُمثِّل كائنٌ من النوع NodeList قائمةً من العقدة من النوع Node، ولكنه لا يَستخدِم واجهة برمجة التطبيقات الخاصة بالقوائم والمُعرَّفة بإطار عمل جافا للتجميعات. يُعرِّف الصنف NodeList بدلًا من ذلك التابعين التاليين: nodeList.getLength()‎ و nodeList.item(i)‎؛ حيث يُعيد الأول عدد العقد ضمن القائمة؛ بينما يُعيد الثاني العقدة الموجودة بموضع i، والذي تتراوح قيمه من 0 حتى nodeList.getLength() - 1. يُعيد التابع nodeList.get()‎ قيمةً من النوع Node، والتي يُمكِن تحويلها type-cast إلى نوعٍ مُخصّصٍ من العقد قبل استخدامها.

بناءً على هذه المعلومات، يُمكِنك إجراء أغلب أنواع العمليات الممكنة على تمثيلات DOM. لنفحص الآن بعض الشيفرة. لنفترض أنه وبينما تعالج إحدى المستندات، قد توصلت إلى عقدةٍ من النوع Element تُمثِّل العنصر التالي:

<background red='1' green='0.6' blue='0.2'/>

قد يقابلنا العنصر السابق إما أثناء اجتياز المستند باستخدام getChildNodes()‎ أو نتيجةً لاستدعاء التابع getElementsByTagName("background")‎. علينا الآن إعادة إنشاء بنية البيانات التي يُمثِّلها المستند، والتي يُعدّ هذا العنصر جزءًا من بياناتها؛ حيث يُمثِل لون الخلفية تحديدًا بواسطة ثلاث سمات attributes تُمثِّل مكوّنات اللون الأحمر والأخضر والأزرق. إذا كان element متغيرًا يُشير إلى تلك العقدة، يُمكِننا استرجاع اللون بكتابة ما يَلي:

double r = Double.parseDouble( element.getAttribute("red") );
double g = Double.parseDouble( element.getAttribute("green") );
double b = Double.parseDouble( element.getAttribute("blue") );
Color bgColor = Color.color(r,g,b);

لنفترض الآن أن element يشير إلى العقدة المُمثِّلة للعنصر التالي:

<symmetric>true</symmetric>

يُمثِل element في تلك الحالة قيمة مُتغيّر من النوع المنطقي boolean، ولكنها رُمزَّت كأنها محتوًى نصي للعنصر. يُمكِننا استرجاع تلك القيمة من العنصر بكتابة ما يَلي:

String bool = element.getTextContent();
boolean symmetric;
if (bool.equals("true"))
   symmetric = true;
else
   symmetric = false;

لنفكر الآن بمثالٍ يَستخدِم كائنًا من الصنف NodeList. إذا واجهنا العنصر التالي الذي ينبغي تمثيله بقائمة عناصر تنتمي للصنف Point2D:

<pointlist>
   <point x='17' y='42'/>   
   <point x='23' y='8'/>   
   <point x='109' y='342'/>   
   <point x='18' y='270'/>   
</pointlist>

لنفترض أن element يشير إلى العقدة المُمثِّلة للعنصر <pointlist>، علينا الآن إنشاء قائمةٍ من النوع ArrayList<Point2D>‎ لتمثيله، حيث تجتاز الشيفرة التالية قائمة الصنف NodeList المُتضمِّنة لعقد أبناء العنصر على النحو التالي:

ArrayList<Point2D> points = new ArrayList<>();
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
   Node child = children.item(i);   // أحد عقد أبناء العنصر
   if ( child instanceof Element ) {
      Element pointElement = (Element)child;  // One of the <point> elements.
      int x = Integer.parseInt( pointElement.getAttribute("x") );
      int y = Integer.parseInt( pointElement.getAttribute("y") );
       // ‫أنشئ النقطة التي يُمثِلها pointElement
      Point2D pt = new Point2D(x,y); 
      points.add(pt);  // أضف النقطة إلى قائمة النقاط
   }
}

تُعدّ جميع عناصر <point> الواقعة داخل العنصر <pointlist> أبناءً له. يجب أن نَستخدِم تعليمة if -كما بالأعلى- نظرًا لإمكانية احتواء العنصر على أبناء تنتمي لأصناف أخرى غير الصنف Element، والتي نحن في الواقع غير مهتمين بمعالجتها ضمن هذا المثال.

يُمكِننا توظيف كل تلك التقنيات لكتابة تابعٍ يقرأ ملف الدْخَل بالبرنامج التوضيحي SimplePaintWithXML.java. عندما نُنشِئ بنية بياناتٍ لتمثيل ملف XML، يُفضَّل أن نبدأ ببنية بياناتٍ افتراضية، والتي يُمكِننا تعديلها، والإضافة إليها بينما نجتاز شجرة DOM المُمثِلة للملف. في الواقع، هذه العملية ليست سهلة نوعًا ما، ولذلك حاول أن تقرأها بعناية:

Color newBackground = Color.WHITE;
ArrayList<CurveData> newCurves = new ArrayList<>();
Element rootElement = xmldoc.getDocumentElement();
if ( ! rootElement.getNodeName().equals("simplepaint") )
    throw new Exception("File is not a SimplePaint file.");
String version = rootElement.getAttribute("version");
try {
    double versionNumber = Double.parseDouble(version);
    if (versionNumber > 1.0)
        throw new Exception("File requires a newer version of SimplePaint.");
}
catch (NumberFormatException e) {
}
NodeList nodes = rootElement.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
   if (nodes.item(i) instanceof Element) {
      Element element = (Element)nodes.item(i);
      if (element.getTagName().equals("background")) {
         double r = Double.parseDouble(element.getAttribute("red"));
         double g = Double.parseDouble(element.getAttribute("green"));
         double b = Double.parseDouble(element.getAttribute("blue"));
         newBackground = Color.color(r,g,b);
      }
      else if (element.getTagName().equals("curve")) {
         CurveData curve = new CurveData();
         curve.color = Color.BLACK;
         curve.points = new ArrayList<>();
         newCurves.add(curve);
         NodeList curveNodes = element.getChildNodes();
         for (int j = 0; j < curveNodes.getLength(); j++) {
           if (curveNodes.item(j) instanceof Element) {
             Element curveElement = (Element)curveNodes.item(j);
             if (curveElement.getTagName().equals("color")) {
               double r = Double.parseDouble(curveElement.getAttribute("red"));
               double g = Double.parseDouble(curveElement.getAttribute("green"));
               double b = Double.parseDouble(curveElement.getAttribute("blue"));
               curve.color = Color.color(r,g,b);
             }
             else if (curveElement.getTagName().equals("point")) {
               double x = Double.parseDouble(curveElement.getAttribute("x"));
               double y = Double.parseDouble(curveElement.getAttribute("y"));
               curve.points.add(new Point2D(x,y));
             }
             else if (curveElement.getTagName().equals("symmetric")) {
               String content = curveElement.getTextContent();
               if (content.equals("true"))
                 curve.symmetric = true;
             }
           }
         }
      }
   }         
}
backgroundColor = newBackground;
curves = newCurves;

يُمكِنك الإطلاع على كامل الشيفرة المصدرية في الملف SimplePaintWithXML.java.

تطوَّرت XML لتُصبِح واحدةً من أهم التقنيات المُستخدَمة لتطوير بعض أعقد التطبيقات، وهناك مع ذلك بعض الأفكار الأساسية البسيطة التي يَسهُل تطبيقها بجافا. بمجرد إطلاعك على أساسيات XML، يُمكِنك استخدامها بفعالية ضمن برامج جافا الخاصة بك.

ترجمة -بتصرّف- للقسم Section 5: A Brief Introduction to XML من فصل Chapter 11: Input/Output Streams, Files, and Networking من كتاب Introduction to Programming Using Java.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...