--
CharinyaKlakhang
ฝน เยี่ยมมากครับ ...P'Jung
แนะนำ
REXML ย่อมาจาก Ruby Electric
XML เป็น library ที่ถูกเขียนโดย Sean Russell ซึ่งมันไม่ได้เป็นเพียงแค่
XML library เท่านั้นแต่ยังเป็นที่นิยม ถูกเขียนด้วยภาษา Ruby 100% ที่ประกอบด้วย DTD-compliant document parsing, XPart querying และ document generation
REXML สนับสนุนการประมวลผลเอกสาร(document processing) ทั้ง tree-based(ของ DOM) และ stream-based (ของ SAX) ซึ่งจะพบได้ทุกๆ platform ที่สนับสนุน Ruby
REXML หลีกเลี่ยงจาก DOM API ที่ทำให้ API กลายเป็นเรื่องยาก จะจัดหา DOM model เอาไว้ให้ แต่เป็น Ruby-ized. มันเป็น
XML API oriented สำหรับ โปรแกรมเมอร์ Ruby ไม่ใช่ โปรแกรมเมอร์
XML ที่มาจาก JAVA
โดย Russell ได้กล่าวว่า
Russell comments:
I have this problem: I dislike obscifucated [sic] APIs. There are several XML parser APIs for Java. Most of them follow DOM or SAX, and are very similar in philosophy with an increasing number of Java APIs. Namely, they look like they were designed by theorists who never had to use their own APIs. The extant XML APIs, in general, suck. They take a markup language which was specifically designed to be very simple, elegant, and powerful, and wrap an obnoxious, bloated, and large API around it. I was always having to refer to the API documentation to do even the most basic XML tree manipulations; nothing was intuitive, and almost every operation was complex.
ข้อแตกต่างโดยทั่วไปบางส่วนของ Ruby API การใช้ block enumerations ด้วย iterators
for (Enumeration e=parent.getChildren(); e.hasMoreElements(); ) {
Element child = (Element)e.nextElement(); // Do something with child
}
parent.each_child{ |child| # Do something with child }
การติดตั้ง
คุณไม่ต้องติดตั้งอะไรอีกเลย ถ้ากำลังใช้ Ruby เวอร์ชั่น 1.8 ขึ้นไป เพราะมี
REXML แล้ว หรือถ้าต้องการอัพเกรดจาก
REXML distribution ก็ให้ใช้คำสั่ง
ruby bin/install.rb หรือถ้าต้องการยกเลิกการติดตั้งก็ใช้คำสั่ง
ruby bin/install.rb -u
การใช้งาน
โปรแกรมเมอร์ส่วนใหญ่ ที่ต้องจัดการเอกสาร
XML ต้องการเรียกใช้หรือดึงข้อมูล จากรูปแบบโครงสร้างที่ง่าย ไม่ซับซ้อน ซึ่ง DOM จะยาก ส่วน SAX ยากกว่า ซึ่งการอธิบายต่อไปนี้ จะอ้างถึงความง่ายและความชัดเจนในการเขียน xml_objectify module ใน python โดยอ้างอิงถึงเอกสาร
address.xml
How to refer to nested data using xml_objectify :
>>> from xml_objectify import XML_Objectify
>>> addressbook = XML_Objectify('address.xml').make_instance()
>>> print addressbook.person[1].address.city
New York
เราจำเป็นต้องรู้รูปแบบของข้อมูลบ้าง ,รู้ว่า root ของเอกสาร คือ address book (ไม่จำเป็นต้องเป็น <addressbook>) และรู้ว่าในเอกสารนั้นๆสามารถแสดงลิสต์ได้หลายคน (อาจมีแค่คนเดียวก็ได้ เพราะสามารถอ้างได้จาก addressbook.person หรือ addressbook.person[0]) เพียงแค่ตอนนี้คุณพอรู้ concept ว่า
มีคนได้หลายคน แต่ละคนมีได้หลายที่อยู่ และแต่ละที่อยู่นั้นๆก็จะมีเมืองต่างๆกันไป (persons have addresses and addresses have cities)
ในทางตรงกันข้าม DOM, จะแนะนำ OOP-ified
XML ที่จะทำงานในลักษณะนั้น โดยอย่างแรกจะอ้างถึง root element ซึ่งสามารถทำได้ 5 แบบ ดังนี้
Using DOM to name the XML document root :
>>> from xml.dom import minidom
>>> dom = minidom.parse('address.xml')
>>> dom.firstChild
<DOM Element: addressbook at 1811436>
>>> dom._get_documentElement()
<DOM Element: addressbook at 1811436>
>>> dom._get_firstChild()
<DOM Element: addressbook at 1811436>
>>> dom.getElementsByTagName('addressbook')[0]
<DOM Element: addressbook at 1811436>
>>> dom.childNodes[0]
<DOM Element: addressbook at 1811436>
โดยคุณจำเป็นต้องคาดเดา(อาจหลายครั้ง)ว่า method และ attribute คืออะไร(หรือดูจากคู่มือ)
คุณจำเป็นต้องก็ทำคาดคะเนหลายๆครั้งเกี่ยวกับแน่นอนและวิธีคืออะไรและคุณลักษณะคืออะไร(หรือทำทิ้งไว้คู่มือสะดวก) เราต้องการรู้ root element ซึ่งทางเลือกที่ดีที่สุด คือได้มาจาก method ._get_documentElement() ถ้าเราต้องการข้อมูลชั้นที่ 2 (city) ของคนจาก address book
How to refer to nested data using DOM:
>>> addressbook = dom._get_documentElement()
>>> print addressbook.getElementsByTagName('person')[1].\
.. getElementsByTagName('address')[0].getAttribute('city')
New York
ซึ่งรูปแบบนี้ค่อนข้างจะเยิ่นเย้อ แต่อย่างไรก็ตามก็เป็นการใช้ DOM มากที่สุด คุณอาจจะใช้ .childNodes attribute array ที่จะ save อักษรหรือผลัพธ์เพียงเล็กน้อย ซึ่งไม่มีประสิทธิภาพ เช่น มี children ใน
นอกจาก ดังนั้นคุณต้องรู้รายละเอียดขนาดที่ว่า city เป็น attribute
การใช้ในรูปแบบ tree mode
REXML จะสนับสนุนรูปแบบการประมวลผลเอกสาร XML 2 แบบที่แตกต่างกัน คือ "tree" และ "stream" โดยอันแรกจะคล้ายๆกับ DOM ส่วนอันที่สอง จะคล้ายกับ SAX โดยเราจะมาดูที่รูปแบบ tree ก่อน
ถ้าต้องการที่จะดึงเอกสาร XML (ใช้ address book) เหมือนตัวอย่างก่อนหน้านี้
How to refer to nested data using REXML :
ruby> require "rexml/document"
ruby> include !REXML%BR%
ruby> addrbook = (Document.new File.new "address.xml").root
ruby> persons = addrbook.elements.to_a("//person")
ruby> puts persons[1].elements["address"].attributes["city"]
New York
จะเห็นว่า expression นี้ดีกว่า โดย method .to_a() สร้าง array ของ element ทั้งหมดในเอกสาร โดย element มีบางสิ่งที่คล้าย DOM แต่ว่าก็ใกล้เคียงกับ XML เองด้วย ซึ่ง argument ใน .to_a() คือ XPath ในกรณีนี้จะหมายถึงทุกอย่างของ ในเอกสาร ถ้าหากต้องการใช้แค่ชั้นแรก หรือ element แรก ต้องทำดังนี้
Creating an array of matching elements :
ruby> persons = addrbook.elements.to_a("/addressbook/person")
เราสามารถใช้ XPaths เป็นตัวชี้ไปยัง element หรือ attribute โดยตรงสำหรับเรียกใช้เลยก็ได้ ดังนี้
Another way to refer to nested data using REXML :
ruby> puts addrbook.elements["//person[2]/address"].attributes["city"]
New York
ข้อสังเกตุ : เราสามารถดูมากกว่านี้ โดยการใช้ REXML elements
Displaying the XML source of elements with REXML :
ruby> puts addrbook.elements["//person[2]/address"]
<address city='New York' street='118 St.' number='344' state='NY'/>
ruby> puts addrbook.elements["//person[2]/contact-info"]
<contact-info>
<email address='robb@iro.ibm.com'/>
<home-phone number='03-3987873'/>
</contact-info>
ยิ่งไปกว่านั้น XPaths ไม่จำเป็นต้องได้ผลลัพธ์แค่ 1 element เราสามารถเห็นได้หลายผลลัพธ์ใน array persons ดังนี้
Matching multiple elements with XPaths :
ruby> puts addrbook.elements.to_a("//person/address[@state='CA']")
<address city='Sacramento' street='Spruce Rd.' number='99' state='CA'/>
<address city='Los Angeles' street='Pine Rd.' number='1234' state='CA'/>
ในทางตรงกันข้าม การชี้ที่ attribute .element จะมีเพียงแค่ 1 ผลลัพธ์ ที่พบก่อนออกมาแสดงเท่านั้น โดยจะเปรียบเทียบกับการใช้ XPaths โดยจะต้องมีการระบุเพื่อให้ element แรกออกมาแสดงเท่านั้น(จึงจะได้ผลลัพธ์เหมือนกัน) ดังนี้
When XPaths match only the first occurrence :
ruby> puts addrbook.elements["//person/address[@state='CA']"]
<address city='Sacramento' street='Spruce Rd.' number='99' state='CA'/>
ruby> puts addrbook.elements.to_a("//person/address[@state='CA']")[0]
<address city='Sacramento' street='Spruce Rd.' number='99' state='CA'/>
โดยที่มาของ XPaths อาจจะต้องถูกเรียกผ่านทาง XPath class ใน REXML ซึ่งมี method เช่น .first(), .each(), and .match()
ซึ่ง method หนึ่งทรู้จักดี ของ REXML element คือ .each iterator ใน Ruby มีโครงสร้างการวนลูปสำหรับจัดการข้อมูลที่เป็น collection โปรแกรมเมอร์ภาษา Ruby ชอบที่จะใช้ iterator methods มากกว่า โดย 2 โครงสร้างด้านล่าง จะให้ผลที่เหมือนกัน แต่ว่าแบบที่ 2 จะเขียนในลักษณะ Ruby
Iterating through matching XPaths in REXML:
ruby> for addr in addrbook.elements.to_a("//address[@state='CA']")
| puts addr.attributes["city"]
| end
Sacramento
Los Angeles
ruby> addrbook.elements.each("//address[@state='CA']") {
| |addr| puts addr.attributes["city"]
| }
Sacramento
Los Angeles
การใช้ในรูปแบบ stream mode
For purposes of "just working," the tree mode of REXML is probably the easiest approach in the Ruby language. But REXML also offers a stream mode that is like a lighter weight variant of SAX. As with SAX, REXML gives the application programmer no default data structures from the XML document. Instead, a "listener" or "handler" class is responsible for providing a set of methods that respond to various events in the document stream. These are the usual collection: A tag starts, a tag ends, element text is encountered, and so on.
While stream mode is not nearly as effortless as working in tree mode, it should generally be much faster. The REXML tutorial claims that stream mode is one thousand five hundred times as fast. While I have not attempted to benchmark it, I suspect this is a limit case (my small examples were still instantaneous in tree mode). Either way, the difference in speed is likely to be significant, if speed matters.
Let's look at a very simple example that does the same thing as the "list the California cities" examples above. Extending this to complex document processing is relatively straightforward:
Stream processing XML documents in REXML
ruby> require "rexml/document"
ruby> require "rexml/streamlistener"
ruby> include REXML
ruby> class Handler
| include StreamListener
| def tag_start name, attrs
| if name=="address" and attrs.assoc("state")[1]=="CA"
| puts attrs.assoc("city")[1]
| end
| end
| end
ruby> Document.parse_stream((File.new "address.xml"), Handler.new)
Sacramento
Los Angeles
One thing to note in the stream processing example is that tag attributes are passed as an array of arrays, which is slightly more work to handle than a hash would be (but is probably faster to create within the library).
แหล่งอ้างอิง
http://www-128.ibm.com/developerworks/xml/library/x-matters18.html
http://www.germane-software.com/software/rexml/docs/tutorial.html
http://www.germane-software.com/software/rexml/index.html#id2247299