论坛首页 编程语言技术论坛

像操作ActiveRecord一样操作XML

浏览 6404 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-08-10  

      在开发RESTful应用或者实现多个应用系统交互时,经常会用到XML作为数据交换格式,系统会经常处理XML数据,采用ROXML将会是一个不错的方案,ROXML是一个实现ruby对象和XML进行映射的库,通过annotations声明,它也可以将xml数据节点映射为ruby类的属性,以及把XML的节点关系映射为ruby类之间的关系,个人觉得,它的作用类似于ActiveRecord将ruby对象映射为数据库表。
它可以:
    1 读取xml生成ruby对象
    2 将ruby对象输出为xml格式
    3 很灵巧的映射配置,包括一对一以及一对多
    4 支持基于UTF-8的多语言文档

在此处可以下载该库 http://rubyforge.org/projects/roxml ,这里有一个图书馆和图书的例子,以此说明ROXML处理一对多的情况。

 

生成相应的ruby类

 

class Book
   include ROXML

   xml_attribute :isbn, "ISBN"
   xml_text :title
   xml_text :description, nil, ROXML::TAG_CDATA
   xml_text :author
end

class Library
   include ROXML

   xml_text :name, "NAME", ROXML::TAG_CDATA
   xml_object :books, Book, ROXML::TAG_ARRAY, "books"
end

 

 

分别创建Library对象和Book对象,并将book放入library中,最后保存输出。

 

book = Book.new()
  book.isbn = "0201710897"
  book.title = "The PickAxe"
  book.description = "Best Ruby book out there!"
  book.author = "David Thomas, Andrew Hunt, Dave Thomas"

  lib = Library.new()
  lib.name = "Favorite Books"
  lib.books << book
File.open("library.xml", "w") do |f|
   lib.to_xml.write(f, 0)
end

 

 可以看到生成的xml为:

 

<library> 
  <NAME><![CDATA[Favorite Books]]></NAME>
  <books>   
   <book ISBN='0201710897'>   
     <title>The PickAxe</title>   
     <description><![CDATA[Best Ruby book out there!]]></description>  
     <author>David Thomas, Andrew Hunt, Dave Thomas</author>   
    </book> 
  </books>
</library>

 

同理,也可以根据xml数据创建ruby对象,这里再举个一对一的例子,假设有以下的xml文档,描述了图书与作者的关系。

 

<book isbn="0974514055"> 
  <title>Programming Ruby - 2nd Edition</title>
  <description>Second edition of the great book</description>  
  <publisher>
     <name>Pragmatic Bookshelf</name> 
   </publisher>
</book>

 

那么生成ruby类与之对应:

 

class BookWithPublisher
  include ROXML 
  xml_name :book 
  xml_object :publisher, Publisher
end

 

最后可以读取先前的xml文档并生成相应的ruby对象了,此时可以操作book对象来达到对xml文档进行处理。

 

book = Book.parse(File.read("book.xml"))

 

 

   发表时间:2008-11-15  
xml变成了hash后如何比较?

通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。
0 请登录后投票
   发表时间:2008-11-15  
CharlesCui 写道
xml变成了hash后如何比较?

通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。

 

if (h1.keys).eql?(h2.keys)

0 请登录后投票
   发表时间:2008-11-15  
liuqiang 写道
CharlesCui 写道
xml变成了hash后如何比较?

通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。

 

if (h1.keys).eql?(h2.keys)


没用,你怎么知道一个hash有多少个key呢?hash里面还会有hash的。
0 请登录后投票
   发表时间:2008-11-15  
CharlesCui 写道
xml变成了hash后如何比较?

通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。


  • Hash 是无序的,不存在“键值对顺序”的说法,http://ruby-doc.org/core/classes/Hash.html#M002875
  • 在 Hash#== 中,体会一下递归的作用
  • Module#== 方法的实现原理,尤其是对于复杂的对象,在 override 的时候需要做什么
0 请登录后投票
   发表时间:2008-11-15   最后修改:2008-11-15
hozaka 写道




跑下题,ruby1.9的key按插入顺序排列了:
http://intertwingly.net/slides/2008/oscon/ruby19/22
这个特性是fiber之后最吸引我的了,会大大加强语言的表达能力。

0 请登录后投票
   发表时间:2008-11-16   最后修改:2008-11-16
hozaka 写道
CharlesCui 写道
xml变成了hash后如何比较?

通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。


  • Hash 是无序的,不存在“键值对顺序”的说法,http://ruby-doc.org/core/classes/Hash.html#M002875
  • 在 Hash#== 中,体会一下递归的作用
  • Module#== 方法的实现原理,尤其是对于复杂的对象,在 override 的时候需要做什么



我没说hash是有序的,我是说ruby内建的==方法可能是有序的。

0 请登录后投票
   发表时间:2008-11-16   最后修改:2008-11-16
还有,

liusong1111,你上面介绍的操作xml的库和activeSupport这个库都有一个类似的方法就是Hash.frmo_xml.

我一直想问的问题是两个只有element顺序不同的xml片段如何比较?
你的意思是不是变成hash后再用eql?或者==来比较?可问题是,hash的子元素的类型不一定啊,说不定是数组或者别的呢,如果是数组,那就肯定不相同了,数组是有顺序的。

你试试下面的代码就理解我的意思了:

require 'rubygems'
require 'pp'
require 'active_support'

xml1=<<-XML
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
XML

xml2=<<-XML
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-value>false</param-value>
            <param-name>listings</param-name>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
XML
h1=Hash.from_xml xml1
h2=Hash.from_xml xml2

pp h1
p "*"*100
pp h2
p "*"*100
p h1==h2
0 请登录后投票
   发表时间:2008-11-16  

我对你的需求的理解是,有2个哈希,哈希可能还存在嵌套,只要这2个哈希的key,包括嵌套的hash的key相同,顺序可以不同,那么这2个hash则认为是相等的。

 

貌似ruby没有内置的api可以用,我觉得还是设计一个小算法自己做,我的思路是遍历hash中的所有key,包括嵌套hash的key,将第一层hash的key以及它的子哈希的key组成一个字符串,并以逗号隔开,再这些字符串存进数组,最后通过比较数组来确定这2个hash是否相等。

 

测试代码如下:

 

class A
 @@a = [] 
 @@str = "" 
 def A.xxx(h)
  h.each do |key, val|
    if val.class.to_s  == "Hash"
      @@str = @@str + key.to_s + ","
      xxx(val)
    else
      @@str = @@str + key.to_s 
      @@a << @@str    
      @@str = ""
    end
  end
 end

 def A.getA()
   return @@a
 end

 def A.setA()
   @@a = []
 end
end

h1 = {'a1'=>1,'a2'=>{'b2'=>1},'a3'=>{'b3'=>{'c3'=>1}}}
h2 = {'a3'=>{'b3'=>{'c3'=>1}},'a1'=>1,'a2'=>{'b2'=>2}}

A.setA 
A.xxx(h1)
a = A.getA
puts A.getA

A.setA 
A.xxx(h2)
b = A.getA
puts b

puts a.eql?(b)

 

 

写的很差,仅供参考

0 请登录后投票
   发表时间:2008-11-16   最后修改:2008-11-16
liuqiang11 写道
我对你的需求的理解是,有2个哈希,哈希可能还存在嵌套,只要这2个哈希的key,包括嵌套的hash的key相同,顺序可以不同,那么这2个hash则认为是相等的。


强强能够理解我的意思,但是你为什么不考虑value啊?差一半就被你搞定了。

我再给强强补充下:

如果有如下两个hash分别为h1和h2,那该如何比较呢?

h1={"servlet"=>
  {"load_on_startup"=>"1",
   "servlet_name"=>"default",
   "init_param"=>
    [{"param_name"=>"debug", "param_value"=>"0"},
     {"param_name"=>"listings", "param_value"=>"false"}],
   "servlet_class"=>"org.apache.catalina.servlets.DefaultServlet"}}
"****************************************************************************************************"
h2={"servlet"=>
  {"load_on_startup"=>"1",
   "servlet_name"=>"default",
   "init_param"=>
    [{"param_name"=>"listings", "param_value"=>"false"},
     {"param_name"=>"debug", "param_value"=>"0"}],
   "servlet_class"=>"org.apache.catalina.servlets.DefaultServlet"}}



现实中的问题是,从一个xml解析而来的hash你不会知道它每个key的value类型,可能是hash,也可能是数组,或者是别的。
请给出一个通用的方法。

强强,再加油一把,期待你的完美表现!

PS:请以我之前给出的那两个xml片段来比较吧,这两个hash就是从那两个hash解析来的。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics