`
liuqiang
  • 浏览: 159466 次
  • 性别: Icon_minigender_1
  • 来自: 华东
社区版块
存档分类
最新评论

像操作ActiveRecord一样操作XML

    博客分类:
  • Ruby
阅读更多

      在开发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"))

 

 

分享到:
评论
11 楼 jimmy 2009-03-11  
devercn 写道
liuqiang,你好,看了你关于ROXML的介绍之后,下载试用,发现ROXML对中文的支持不好,to_xml出来的结果都是乱码,不知你遇到没有,请问如何解决?


require "roxml"
module ROXML
  class XMLAttributeRef < XMLRef # :nodoc:
  private
    # Updates the attribute in the given XML block to
    # the value provided.
    def write_xml(xml, value)
      xml.attributes[name] = value.to_s
    end
  end

  class XMLTextRef < XMLRef # :nodoc:
    delegate :cdata?, :content?, :name?, :to =>pts
  private
    def add(dest, value)
      if cdata?
        dest.child_add(XML::Node.new_cdata(value.to_s))
      else
        dest.content = value.to_s
      end
    end
  end
end


另外
1、在你代码中加 $KCODE = 'utf8'

2、打开保存xml时加encoding

doc.save("library.xml", :encoding =>  LibXML::XML::Encoding::UTF_8)

doc = LibXML::XML::Document.file("library.xml", :encoding =>  LibXML::XML::Encoding::UTF_8)
lib = Library.from_xml(doc)


10 楼 devercn 2009-03-11  
liuqiang,你好,看了你关于ROXML的介绍之后,下载试用,发现ROXML对中文的支持不好,to_xml出来的结果都是乱码,不知你遇到没有,请问如何解决?
9 楼 CharlesCui 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解析来的。
8 楼 liuqiang 2008-11-16  
<p>我对你的需求的理解是,有2个哈希,哈希可能还存在嵌套,只要这2个哈希的key,包括嵌套的hash的key相同,顺序可以不同,那么这2个hash则认为是相等的。</p>
<p> </p>
<p>貌似ruby没有内置的api可以用,我觉得还是设计一个小算法自己做,我的思路是遍历hash中的所有key,包括嵌套hash的key,将第一层hash的key以及它的子哈希的key组成一个字符串,并以逗号隔开,再这些字符串存进数组,最后通过比较数组来确定这2个hash是否相等。</p>
<p> </p>
<p>测试代码如下:</p>
<p> </p>
<pre name='code' class='ruby'>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 &lt;&lt; @@str   
      @@str = ""
    end
  end
end

def A.getA()
   return @@a
end

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

h1 = {'a1'=&gt;1,'a2'=&gt;{'b2'=&gt;1},'a3'=&gt;{'b3'=&gt;{'c3'=&gt;1}}}
h2 = {'a3'=&gt;{'b3'=&gt;{'c3'=&gt;1}},'a1'=&gt;1,'a2'=&gt;{'b2'=&gt;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)</pre>
<p> </p>
<p> </p>
<p>写的很差,仅供参考</p>
7 楼 CharlesCui 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
6 楼 CharlesCui 2008-11-16  
hozaka 写道
CharlesCui 写道
xml变成了hash后如何比较?

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


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



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

5 楼 liusong1111 2008-11-15  
hozaka 写道




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

4 楼 hozaka 2008-11-15  
CharlesCui 写道
xml变成了hash后如何比较?

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


  • Hash 是无序的,不存在“键值对顺序”的说法,http://ruby-doc.org/core/classes/Hash.html#M002875
  • 在 Hash#== 中,体会一下递归的作用
  • Module#== 方法的实现原理,尤其是对于复杂的对象,在 override 的时候需要做什么
3 楼 CharlesCui 2008-11-15  
<div class='quote_title'>liuqiang 写道</div><div class='quote_div'><div class='quote_title'>CharlesCui 写道</div>
<div class='quote_div'>xml变成了hash后如何比较?<br/><br/>通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。</div>
<p> </p>
<p>if (h1.keys).eql?(h2.keys)</p></div><br/>没用,你怎么知道一个hash有多少个key呢?hash里面还会有hash的。
2 楼 liuqiang 2008-11-15  
<div class='quote_title'>CharlesCui 写道</div>
<div class='quote_div'>xml变成了hash后如何比较?<br/><br/>通过内建的==是无法比较两个有相同键值对的hash的,因为只要键值对顺序不同,==方法就认为不同。</div>
<p> </p>
<p>if (h1.keys).eql?(h2.keys)</p>
1 楼 CharlesCui 2008-11-15  
xml变成了hash后如何比较?

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

相关推荐

    Ruby-HappyMapper允许您快速轻松地解析XML数据并将其转换成ruby的数据结构

    通过定义简单的类和模块,HappyMapper能够自动将XML元素映射到Ruby类的属性上,这样我们就可以像操作普通的Ruby对象一样操作解析后的XML数据。 在实际应用中,首先我们需要包含HappyMapper库,并创建一个继承自...

    Castle简单实例

    ActiveRecord模式将数据存储的概念与业务对象结合,使得开发者可以像操作普通对象一样操作数据库记录,降低了数据库操作的复杂性。 ** Castle简单实例** 在这个"Castle简单实例"中,我们可以预期看到如何使用...

    Yii框架详解

    ActiveRecord将数据库表映射为对象,使得数据库操作就像操作对象属性一样简单。 六、RESTful API支持 Yii框架对构建RESTful Web服务提供了很好的支持,可以方便地创建和消费JSON或XML格式的API。这在现代Web应用和...

    springboot和JFinal的集成

    Record模式允许开发者直接对数据库表进行操作,就像操作Java对象一样,极大地提高了开发效率。同时,JFinal还提供了拦截器、插件等机制,增强了灵活性和可扩展性。 在将Spring Boot和JFinal集成时,首先需要在...

    xUtils 3.5.0资源包 源码 jar包

    DB模块是xUtils内置的数据库操作组件,基于ActiveRecord模式,让数据库操作如同操作Java对象一样简单。只需定义数据模型类,xUtils就能自动创建表、插入、查询、更新和删除数据。 6. **IO模块** IO模块提供了便捷...

    ruby on rails api

    它实现了对象关系映射(ORM),将数据库表映射为Ruby类,使得开发者可以像操作普通对象一样操作数据库记录。 2. **ActionController**:处理HTTP请求,并调用模型来执行业务逻辑,然后将结果传递给视图进行渲染。...

    ruby_user's_guide

    例如,你可以对字符串进行操作,就像对待一个对象一样,调用它的方法如`length`来获取字符串的长度。 Ruby的语法简洁明了,易于学习。它采用了块(Blocks)和闭包(Closures)的概念,这使得处理迭代和异步操作变得...

    issue_4:応用4

    ActiveRecord提供了丰富的查询接口,包括链式查询,使得数据库操作如同操作Ruby对象一样直观。 Ruby的元编程能力是其独特之处,它允许在运行时修改和创建类及方法,这在其他语言中并不常见。这种能力使得Ruby代码...

    Well-child-Space

    Rails的ActiveRecord简化了与数据库的交互,通过ORM(对象关系映射)将数据库表与Ruby类关联起来,使得数据操作如同操作对象一样简单。 另外,为了保证用户的安全,Rails内置了CSRF(跨站请求伪造)和XSS(跨站脚本...

    ruby-jogging:ruby 的每日提交

    这个项目可能包含一系列的练习、挑战或者小任务,鼓励用户每天投入一定时间进行学习和编码,就像慢跑一样,每天坚持一小段距离,日积月累,可以显著提高健康状况。在Ruby的世界里,这种持续的练习同样能够提升编程...

Global site tag (gtag.js) - Google Analytics