برمجة جامباس/التعامل مع وثائق XML


التركيب الرئيسي لوثيقة XML

عدل

جميع وثائق XML تبدأ بـ

 <?xml version=”1.0” encoding=”UTF-16”?>

والتي تشير إلى أن ما تبقى من الوثيقة تحتوي على بيانات إكس إم إل ، ويحدد الإصدار وترميز اليونيكود المستخدمين. في الإصدار 1.0 يمكنك حذف الإعلان إكس إم إل ، ولكن في الإصدار 1.1 الإعلان إلزامي إلزامي. يتبع الإعلان في وثيقة XML العنصر 'root' والذي من الممكن أن يحتوي على أي عدد من العناصر الفرعية والمحصورة بين أوسمة البداية والنهاية. من الممكن أن تحتوي العناصر على عدة سمات, ولايمكن أن تظهر السمة أكثر من مرة واحدة في العنصر الواحد. يجب أن تتداخل العناصر بشكل صحيح ، وبالتالي يجب أن تغلق بالترتيب ذاته الذي فتحت به. التعليقات تبدأ بـ <! - وتنتهي بـ ->. انظر إلى مستند' XML المثالي .

<?xml version=”1.1” encoding=”UTF-16” ?>
 < !-- The line above is the XML declaration -- >
 < !-- This line and the preceding are comments -- >
 <root>
   <element_name attribute_name = "attribute_value">
     Element content
   </element_name>
 </root>

كما ترى, فقد تم فصل المستند بعدة سطور وهناك بعض المسافات البادئة للدلالة على مختلف مستويات التداخل ، ولكن هذا ليس ضروريا ، يمكن أن تحتوي أي وثيقة من سطر واحد من النص. لكن التنسيق بهذه الطريقة تساعدك على فهم المستند. سوف تجد أنه من المفيد جدا تنسيق وثائق XML عندما تحتاج إلى قراءة أو تحرير الوثيقة في محرر نصوص عادي.

كتابة وثيقة XML

عدل

ستبدأ بكتابة برنامج يقوم بإنشاء وثيقة XML التالية بناء على واحدة من الوظائف التي سبق أن عملت عليها في درس العناصر .

 <?xml version=”1.0” encoding=”UTF-16” ?>
 <!-- Heroes Characters -->
 <characters serie=”Heroes”>
   <heroe id=”1” name=”Claire Bennet”>
     <name>Claire Bennet</name>
     <played_by>Hayden Panettiere</played_by>
     <ability>
       Rapid cellular regeneration
     </ability>
   </heroe>
   <heroe id=”2” name=”Hiro Nakamura”>
     <name>Hiro Nakamura</name>
     <played_by>Masi Oka</played_by>
     <ability>
       Space-time manipulation: teleportation & time travel
     </ability>
   </heroe>
   <villain id=”1” name= Gabriel Sylar”>
     <name>Gabriel Sylar</name>
     <played_by>Zachary Quinto</played_by>
     <ability>
       Understand how things work and multiple other abilities acquired
     </ability>
   </villain>
 </characters>

قم بإنشاء تطبيق نصي جديد وسمه WriteXML, تأكد من أنك أشرت على عنصر XML/XSLT ضمن خيارات المشروع.بتفعيلك لهذا الخيار، فأنت تخبر Gambas بحاجتك للعنصرين gb.xml و gb.xml.xslt وسيقوم بإضافتهما إلى المشروع.سنقوم بكتابة وظيفة جديدة ضمن MMain سيم فتح عنصر XmlWriter لكتابة وحفظ المستند Heroes.xml في مجلد المنزل وسيتم إنشائه شاملا المسافات البادئة والتي ذكرناها سابقا .

 Dim writer as XmlWriter
 writer = NEW XmlWriter
 writer.Open(User.Home & /Heroes.xml, TRUE)
 writer.Comment(Heroes Characters)
  Following code goes here
 writer.EndDocument()

سيتم فتح الملف باستخدام Open وفي الحقيقة لن يتم كتابته إلى مجلد المستخدم حتى يتم إستدعاءEndDocument . هذه الوظيفة, تقوم بإنهاء أي وسم (tag) مفقود وذلك لتحقيق التوافقية مع مبادئ تحريرXML. العنصر الرئيسي هو character, ولتخبر Gambas بذلك سنقوم بكتابة الكود التالي بين التعليمتين Open و EndDocument:

 writer.StartElement(“characters”)
   writer.Attribute(“serie”, “Heroes”)
   ‘Elements code will replace this comment line
 writer.EndElement ‘characters

إعتادت أوراكل على قول “كل ما له بداية له نهاية”وفي حالة XML فذلك صحيح مئة بالمئة. لهذا قمنا بكتابة الوظيفة EndElement مباشرة بعد StartElement التي تسبقها. وهو تمرين جيد أن تقوم بإقفال الجمل التي تحتاج إلى إقفال لاحقا , وذلك لتتجنب صداع تتبع الأخطاء. داخل الوظيفتين StartElement و EndElement , سنكتب أول عنصرheroe.

 writer.StartElement(“heroe”)
   writer.Attribute(“id”, “1”)
   writer.Attribute(“name”, “Claire Bennet”)
     writer.StartElement(“name”)
       writer.Text(“Claire Bennet”)
     writer.EndElement ‘name
     writer.StartElement(“played_by”)
       writer.Text(“Hayden Panettiere”)
     writer.EndElement ‘played_by
     writer.StartElement(“ability”)
       writer.Text(“Rapid cellular regeneration”)
     writer.EndElement ‘ability
 writer.EndElement ‘heroe 1

مباشرة بعد ذلك سنقوم بكتابة عنصر heroe مع بعض الإختصارات التي تحبها.

 writer.StartElement(“heroe”, [“id”, “2”, “name”, “Hiro Nakamura”])
     writer.Element(“name”, “Hiro Nakamura”)
     writer.Element(“played_by”, “Masi Oka”)
     writer.Element(“ability”, “Space-time manipulation: teleportation & time travel”)
 writer.EndElement ‘heroe 2

شغل البرنامج ثم إفتح الملف Heroes.xml بمحرر نصوص أو متصفح إنترنت وسترى وثيقة XML الناتجة.

قراءة XML

عدل

أسهل طريقة لقراءة ملفات XML هي بإستخدام XmlReader والتي نسمح لك بالتنقل داخل المستند بناء على تحليل كل عنصر; وتسمح بمعرفةإسم, نوع وقيمة كل عنصر. تقوم بعملية تكرار إستدعاء Read() وتمريرها للمحلل; وينزل المحلل بشكل متكرر ليعكس هيكل وثيقة XML. سنقوم الآن بإنشاء تطبيق يقرأ ملف XML الذي أنشأناه ويعرض المعلومات فيه على شكل شجرة. سننشئ تطبيق رسومي نسميه ReadXML ونختار العناصر التالية مع إعداداتها. لاتنسى إختيار المكون gb.xml.

Widget Property Setting
TreeView1
Button1 Text “Populate Tree”

عندما يضغط المستخد على زر Populate, سيتم ملء الشجرة بمحتوى ملف Heroes.xml. ولإتمام ذلك, ستقوم بكتابة التالي في الحدث Click() للزرButton1. أولا, نحتاج إلى إنشاء مثيل جديد للعنصر XmlReader, ثم نحاول فتح ملف .xml , إذا لم يتمكن من فتح الملف سيقوم بعرض رسالة خطأ.

 DIM reader AS XmlReader
  reader = NEW XmlReader
  TRY reader.Open(User.home & "/Heroes.xml")
  IF ERROR THEN 
    Message.Error("Error when trying to open the Heroes.XML file!")
    RETURN 
  ENDIF

انتبه إلى أنه بإمكانك التأكد من وجود الملف قبل فتحه باستخدام الوظيفة Exist. Then you need to declare the loop where the pull parser will work. For each loop iteration you’ll call the method Read() that locate the parser on the next node on the XML file. During this process an error can occur, because of this you need to handle any potential error. Also, before to perform any other task we need to verify if the parser reached the end of file has reached in order to exit the loop; and just before exit the procedure you need to close the XML document.

 DO WHILE TRUE
    TRY reader.Read()
    IF reader.Eof THEN BREAK
	The rest of the code goes here
  LOOP 
  reader.Close()

You can anticipate potential errors if you perform some validation on the .xml file just to be sure that the file you are attempting to open actually contains the type of data you expect. If you remember, we discuss some paragraphs ago that the pull parser descent recursively through the XML structure. In order to do this, the application needs to make some decisions based on the name of the Element. If the element “characters” contains an attribute called “series” you’ll add the first item to the Tree view.

 SELECT CASE reader.Node.Name
      CASE "characters"
        FOR EACH reader.Node.Attributes
          IF reader.Node.Name = "series" THEN 
            TRY TreeView1.Add(reader.Node.Value, "Characters of the TV Series: " & reader.Node.Value, NULL, NULL)
            PRINT "Characters of the TV Series: " & reader.Node.Value
          ENDIF 
        NEXT
      Here goes the next block of code
    END SELECT

In order to fill properly the Tree view widget, you’re going to need to declare some variables that are going to help you to keep track of the attributes id and name of each element heroe.

 DIM iNode AS String
  DIM iName AS String

If an element “heroe” or “villain” is found you need to insert the parent item on TreeView1; store the values of the parent item to use them later to insert the sub-elements to the Tree view and move to the next node.

 CASE "heroe", "villain"
        FOR EACH reader.Node.Attributes
          IF reader.Node.Name = "id" THEN iNode = reader.Node.Value
          IF reader.Node.Name = "name" THEN iName = reader.Node.Value
        NEXT 
        IF iNode <> "" AND iName <> "" THEN 
          TRY TreeView1.Add(iNode, iNode & " - " & iName)
          PRINT iNode & " - " & iName
        ENDIF
        TRY reader.Read()
        IF ERROR THEN RETURN

Then the parser must continue looping inside the next nested elements, in this case the elements name, played_by and ability, and add them under the parent item on TreeView1

 DO WHILE TRUE
          IF reader.Node.Type = XmlReaderNodeType.Element THEN 
            SELECT CASE reader.Node.Name
              CASE "name"
                TRY reader.Read()
                TRY TreeView1.Add(iNode & "-n", "Name: " & reader.Node.Value, NULL, iNode)
                PRINT "    Name: " & reader.Node.Value
              CASE "played_by"
                TRY reader.Read()
                TRY TreeView1.Add(iNode & "-p", "Played by: " & reader.Node.Value, NULL, iNode)
                PRINT "    Played by: " & reader.Node.Value
              CASE "ability"
                TRY reader.Read()
                TRY TreeView1.Add(iNode & "-a", "Ability: " & reader.Node.Value, NULL, iNode)
                PRINT "    Ability: " & reader.Node.Value
            END SELECT
After this, the parser must be moved to the next node, and if the end of the element has been reached move to the next node.
            TRY reader.Read()
            IF ERROR THEN BREAK 
          ELSE 
            IF reader.Node.Type = XmlReaderNodeType.EndElement THEN BREAK 
          ENDIF 
          TRY reader.Read()
          IF ERROR THEN BREAK 
        LOOP

ReadXML must look like this.

Archivo:ReadXML.png

If the previous example still kind of confusing probably you must try to understand first how the Read() parser moves over the XML structure. Add a new button, and include the following code and compare the output on the console against the content of the XML file.

 DIM reader AS XmlReader
  reader = NEW XmlReader
  TRY reader.Open(User.home & "/Heroes.xml")
  IF ERROR THEN 
    Message.Error("Error when trying to open the Heroes.XML file!")
    RETURN 
  ENDIF
  DO WHILE TRUE
    TRY reader.Read()
    IF reader.Eof THEN BREAK
	PRINT Node: Type= & reader.Node.Type & , Name= & reader.Node.Name & , Value= & reader.Node.Value
  LOOP 
  reader.Close()

Usar XSLT

عدل

XSLT is a language for transforming XML documents into other XML documents. For instance, you can convert any XML into a HTML document or more complex task as create a PDF file. The XSLT template contains all the required instructions to transform a given XML document into other XML format. You’ll create the following XSLT template to transform Heroes.XML into HTML format. Using a plain text editor write the following XSLT file and save it on the users home folder as xml2xhtml.XSLT.

 <xsl:stylesheet version="2.0"
                 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns="http://www.w3.org/1999/xhtml">
 <xsl:template match="/">
 <html>
   <head>
     <title>Example of XSL Transformation</title>
     <style type="text/css">
       table {padding: 2pt; widht:100%;}
       th {font-weight: bold; background-color: #cccc99; color: #000000}
       tr.odd {background-color: #ffffff; text-align: center}
       tr.even {background-color: #f5f5dc; text-align: center}
     </style>
   </head>
   <body>
     < h1 >Characters of the series: Heroes< /h1 >
     < table >
       < tr >
         < th >Name< /th >
         < th >Played by< /th >
         < th >Ability< /th >
       < /tr >
       <x s l:for-each select="characters/*" >
         <xsl:choose>
           <xsl:when test="position() mod 2 = 0">
             < tr class="even" >
              < td ><xsl:value-of select="name" />< /td >
              < td ><xsl:value-of select="played_by" />< /td >
              < td ><xsl:value-of select="ability" />< /td >
             < /tr >
           </xsl:when>
           <xsl:otherwise>
             < tr class="odd" >
              < td ><xsl:value-of select="name" />< /td >
              < td ><xsl:value-of select="played_by" />< /td >
              < td ><xsl:value-of select="ability" />< /td >
             < /tr >
           </xsl:otherwise>
         </xsl:choose>
       </xsl:for-each>
     < /table >
   </body>
 </html>
 </xsl:template>
 </xsl:stylesheet>

Create a new console application with support for XML & XSLT. For Main() function write the following code:

 DIM docXML AS NEW XmlDocument
  DIM docXSLT AS NEW XmlDocument
  DIM docXHTML AS NEW XmlDocument
  docXML.Open(User.Home & "/Heroes.xml")
  docXSLT.Open(User.Home & "/xml2xhtml.xslt")
  docXHTML = Xslt.Transform(docXML, docXSLT)
  docXHTML.Write(User.Home & "/Heroes.html")

Obviously, you can add any fancy error-handling code you want. Or proactively check if both files (Heroes.xml & xml2xhtml.xslt) exist, or if is enough space on disk to save the resultant XHTML file. Here we’ll focus to understand how the XSLT template works, because as you just see the code in Gambas is pretty straight forward. The application open the two files Heroes.xml & xml2xhtml.xslt, then calls the Transform() method that perform the conversion to XHTML and finally save the file to the specified location. I’m going to skip all HTML related and the XSL headers. The key element of the XSLT language you used is the for-each loop that will navigate thru all the elements arranged under the root tag characters. Inside the loop, the values of the elements name, played_by & ability are accessed using value-of. There is other item of the XSLT language used in this example: the choose-when-otherwise. This was used in order to apply some styling to the HTML report created from the XML document. If you need to learn more about XML, why don’t you try http://www.w3.org/TR/xslt20/