Ruby - บทช่วยสอน XML, XSLT และ XPath

XML คืออะไร?

Extensible Markup Language (XML) เป็นภาษามาร์กอัปเหมือนกับ HTML หรือ SGML สิ่งนี้แนะนำโดย World Wide Web Consortium และมีให้ใช้งานในรูปแบบมาตรฐานเปิด

XML เป็นภาษาโอเพ่นซอร์สแบบพกพาที่ช่วยให้โปรแกรมเมอร์สามารถพัฒนาแอปพลิเคชันที่แอปพลิเคชันอื่นสามารถอ่านได้โดยไม่คำนึงถึงระบบปฏิบัติการและ / หรือภาษาพัฒนาการ

XML มีประโยชน์อย่างยิ่งสำหรับการติดตามข้อมูลจำนวนน้อยถึงปานกลางโดยไม่ต้องใช้แบ็คโบนที่ใช้ SQL

สถาปัตยกรรม XML Parser และ API

มีสองรสชาติที่แตกต่างกันสำหรับตัวแยกวิเคราะห์ XML -

  • SAX-like (Stream interfaces)- ที่นี่คุณลงทะเบียนการโทรกลับสำหรับเหตุการณ์ที่น่าสนใจจากนั้นให้โปรแกรมแยกวิเคราะห์ดำเนินการผ่านเอกสาร สิ่งนี้มีประโยชน์เมื่อเอกสารของคุณมีขนาดใหญ่หรือคุณมีข้อ จำกัด ด้านหน่วยความจำมันจะแยกวิเคราะห์ไฟล์เมื่ออ่านจากดิสก์และไฟล์ทั้งหมดจะไม่ถูกเก็บไว้ในหน่วยความจำ

  • DOM-like (Object tree interfaces) - นี่คือคำแนะนำของ World Wide Web Consortium ซึ่งไฟล์ทั้งหมดจะถูกอ่านลงในหน่วยความจำและจัดเก็บในรูปแบบลำดับชั้น (อิงตามต้นไม้) เพื่อแสดงคุณสมบัติทั้งหมดของเอกสาร XML

เห็นได้ชัดว่า SAX ไม่สามารถประมวลผลข้อมูลได้เร็วเท่า DOM เมื่อทำงานกับไฟล์ขนาดใหญ่ ในทางกลับกันการใช้ DOM เพียงอย่างเดียวสามารถฆ่าทรัพยากรของคุณได้โดยเฉพาะอย่างยิ่งหากใช้กับไฟล์ขนาดเล็กจำนวนมาก

SAX เป็นแบบอ่านอย่างเดียวในขณะที่ DOM อนุญาตให้เปลี่ยนแปลงไฟล์ XML เนื่องจาก API ที่แตกต่างกันทั้งสองนี้เสริมซึ่งกันและกันอย่างแท้จริงจึงไม่มีเหตุผลว่าทำไมคุณจึงไม่สามารถใช้ API ทั้งสองสำหรับโครงการขนาดใหญ่ได้

การแยกวิเคราะห์และสร้าง XML โดยใช้ Ruby

วิธีที่ใช้กันทั่วไปในการจัดการ XML คือการใช้ไลบรารี REXML โดย Sean Russell ตั้งแต่ปี 2002 REXML เป็นส่วนหนึ่งของการกระจาย Ruby มาตรฐาน

REXML เป็นตัวประมวลผล Pure-Ruby XML ที่สอดคล้องกับมาตรฐาน XML 1.0 เป็นตัวประมวลผลที่ไม่ผ่านการตรวจสอบความถูกต้องโดยผ่านการทดสอบความสอดคล้องที่ไม่ผ่านการตรวจสอบ OASIS ทั้งหมด

ตัวแยกวิเคราะห์ REXML มีข้อดีเหนือกว่าตัวแยกวิเคราะห์อื่น ๆ ที่มีดังต่อไปนี้ -

  • มันเขียนด้วยภาษา Ruby 100 เปอร์เซ็นต์
  • สามารถใช้สำหรับการแยกวิเคราะห์ SAX และ DOM
  • มีน้ำหนักเบาโค้ดน้อยกว่า 2,000 บรรทัด
  • วิธีการและชั้นเรียนนั้นเข้าใจง่ายมาก
  • API ที่ใช้ SAX2 และรองรับ XPath แบบเต็ม
  • จัดส่งพร้อมการติดตั้ง Ruby และไม่จำเป็นต้องติดตั้งแยกต่างหาก

สำหรับตัวอย่างโค้ด XML ทั้งหมดของเราให้ใช้ไฟล์ XML แบบธรรมดาเป็นอินพุต -

<collection shelf = "New Arrivals">
   <movie title = "Enemy Behind">
      <type>War, Thriller</type>
      <format>DVD</format>
      <year>2003</year>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Talk about a US-Japan war</description>
   </movie>
   <movie title = "Transformers">
      <type>Anime, Science Fiction</type>
      <format>DVD</format>
      <year>1989</year>
      <rating>R</rating>
      <stars>8</stars>
      <description>A schientific fiction</description>
   </movie>
   <movie title = "Trigun">
      <type>Anime, Action</type>
      <format>DVD</format>
      <episodes>4</episodes>
      <rating>PG</rating>
      <stars>10</stars>
      <description>Vash the Stampede!</description>
   </movie>
   <movie title = "Ishtar">
      <type>Comedy</type>
      <format>VHS</format>
      <rating>PG</rating>
      <stars>2</stars>
      <description>Viewable boredom</description>
   </movie>
</collection>

การแยกวิเคราะห์เหมือน DOM

Let 's แรกแยกข้อมูล XML เราในแฟชั่นต้นไม้ เราเริ่มต้นด้วยการกำหนดให้rexml/documentห้องสมุด; บ่อยครั้งที่เราทำการรวม REXML เพื่อนำเข้าสู่เนมสเปซระดับบนสุดเพื่อความสะดวก

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Now get the root element
root = xmldoc.root
puts "Root element : " + root.attributes["shelf"]

# This will output all the movie titles.
xmldoc.elements.each("collection/movie"){ 
   |e| puts "Movie Title : " + e.attributes["title"] 
}

# This will output all the movie types.
xmldoc.elements.each("collection/movie/type") {
   |e| puts "Movie Type : " + e.text 
}

# This will output all the movie description.
xmldoc.elements.each("collection/movie/description") {
   |e| puts "Movie Description : " + e.text 
}

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

Root element : New Arrivals
Movie Title : Enemy Behind
Movie Title : Transformers
Movie Title : Trigun
Movie Title : Ishtar
Movie Type : War, Thriller
Movie Type : Anime, Science Fiction
Movie Type : Anime, Action
Movie Type : Comedy
Movie Description : Talk about a US-Japan war
Movie Description : A schientific fiction
Movie Description : Vash the Stampede!
Movie Description : Viewable boredom

การแยกวิเคราะห์แบบ SAX

ในการประมวลผลข้อมูลเดียวกันmovies.xmlไฟล์ในรูปแบบที่มุ่งเน้นสตรีมเราจะกำหนดคลาสฟังซึ่งเมธอดจะเป็นเป้าหมายของการเรียกกลับจากโปรแกรมแยกวิเคราะห์

NOTE - ไม่แนะนำให้ใช้การแยกวิเคราะห์แบบ SAX สำหรับไฟล์ขนาดเล็กนี่เป็นเพียงตัวอย่างการสาธิตเท่านั้น

#!/usr/bin/ruby -w

require 'rexml/document'
require 'rexml/streamlistener'
include REXML

class MyListener
   include REXML::StreamListener
   def tag_start(*args)
      puts "tag_start: #{args.map {|x| x.inspect}.join(', ')}"
   end

   def text(data)
      return if data =~ /^\w*$/     # whitespace only
      abbrev = data[0..40] + (data.length > 40 ? "..." : "")
      puts "  text   :   #{abbrev.inspect}"
   end
end

list = MyListener.new
xmlfile = File.new("movies.xml")
Document.parse_stream(xmlfile, list)

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

tag_start: "collection", {"shelf"=>"New Arrivals"}
tag_start: "movie", {"title"=>"Enemy Behind"}
tag_start: "type", {}
   text   :   "War, Thriller"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Talk about a US-Japan war"
tag_start: "movie", {"title"=>"Transformers"}
tag_start: "type", {}
   text   :   "Anime, Science Fiction"
tag_start: "format", {}
tag_start: "year", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "A schientific fiction"
tag_start: "movie", {"title"=>"Trigun"}
tag_start: "type", {}
   text   :   "Anime, Action"
tag_start: "format", {}
tag_start: "episodes", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Vash the Stampede!"
tag_start: "movie", {"title"=>"Ishtar"}
tag_start: "type", {}
tag_start: "format", {}
tag_start: "rating", {}
tag_start: "stars", {}
tag_start: "description", {}
   text   :   "Viewable boredom"

XPath และ Ruby

อีกวิธีหนึ่งในการดู XML คือ XPath นี่คือภาษาหลอกชนิดหนึ่งที่อธิบายวิธีการค้นหาองค์ประกอบและแอตทริบิวต์เฉพาะในเอกสาร XML โดยถือว่าเอกสารนั้นเป็นโครงสร้างที่เรียงลำดับตามตรรกะ

REXML รองรับ XPath ผ่านคลาสXPath โดยถือว่าการแยกวิเคราะห์แบบต้นไม้ (แบบจำลองวัตถุเอกสาร) ดังที่เราได้เห็นข้างต้น

#!/usr/bin/ruby -w

require 'rexml/document'
include REXML

xmlfile = File.new("movies.xml")
xmldoc = Document.new(xmlfile)

# Info for the first movie found
movie = XPath.first(xmldoc, "//movie")
p movie

# Print out all the movie types
XPath.each(xmldoc, "//type") { |e| puts e.text }

# Get an array of all of the movie formats.
names = XPath.match(xmldoc, "//format").map {|x| x.text }
p names

สิ่งนี้จะให้ผลลัพธ์ดังต่อไปนี้ -

<movie title = 'Enemy Behind'> ... </>
War, Thriller
Anime, Science Fiction
Anime, Action
Comedy
["DVD", "DVD", "DVD", "VHS"]

XSLT และ Ruby

มีตัวแยกวิเคราะห์ XSLT สองตัวที่ Ruby สามารถใช้ได้ คำอธิบายสั้น ๆ ของแต่ละรายการมีให้ที่นี่

ทับทิม - ซาบโลตรอน

โปรแกรมแยกวิเคราะห์นี้เขียนและดูแลโดย Masayoshi Takahashi สิ่งนี้เขียนขึ้นสำหรับ Linux OS เป็นหลักและต้องการไลบรารีต่อไปนี้ -

  • Sablot
  • Iconv
  • Expat

คุณสามารถค้นหาโมดูลนี้ได้ที่ Ruby-Sablotron.

XSLT4R

XSLT4R เขียนโดย Michael Neumann และสามารถพบได้ที่ RAA ในส่วน Library ภายใต้ XML XSLT4R ใช้อินเทอร์เฟซบรรทัดคำสั่งที่เรียบง่ายแม้ว่าจะสามารถใช้ภายในแอปพลิเคชันของบุคคลที่สามเพื่อแปลงเอกสาร XML

XSLT4R ต้องการ XMLScan ในการทำงานซึ่งรวมอยู่ในไฟล์เก็บถาวร XSLT4R และซึ่งเป็นโมดูล Ruby 100 เปอร์เซ็นต์ โมดูลเหล่านี้สามารถติดตั้งได้โดยใช้วิธีการติดตั้ง Ruby มาตรฐาน (เช่น Ruby install.rb)

XSLT4R มีไวยากรณ์ต่อไปนี้ -

ruby xslt.rb stylesheet.xsl document.xml [arguments]

หากคุณต้องการใช้ XSLT4R จากภายในแอปพลิเคชันคุณสามารถรวม XSLT และป้อนพารามิเตอร์ที่คุณต้องการได้ นี่คือตัวอย่าง -

require "xslt"

stylesheet = File.readlines("stylesheet.xsl").to_s
xml_doc = File.readlines("document.xml").to_s
arguments = { 'image_dir' => '/....' }
sheet = XSLT::Stylesheet.new( stylesheet, arguments )

# output to StdOut
sheet.apply( xml_doc )

# output to 'str'
str = ""
sheet.output = [ str ]
sheet.apply( xml_doc )

อ่านเพิ่มเติม

  • สำหรับรายละเอียดที่สมบูรณ์เกี่ยวกับ REXML Parser โปรดดูที่เอกสารมาตรฐานREXML แยกวิเคราะห์เอกสาร

  • คุณสามารถดาวน์โหลด XSLT4R จากรา Repository