2008-09-17 64 views
7

给定一个适度复杂的XML结构(数十个元素,数百个属性),没有XSD并且希望创建对象模型,那么避免编写样板from_xml()和to_xml()方法的优雅方法是什么?用于快速和简洁的XML序列化的Ruby代码?

例如,给定:

<Foo bar="1"><Bat baz="blah"/></Foo> 

如何避免写作的无尽的序列:

class Foo 
    attr_reader :bar, :bat 

    def from_xml(el) 
    @bar = el.attributes['bar'] 
    @bat = Bat.new() 
    @bat.from_xml(XPath.first(el, "./bat") 
    end 
etc... 

我不介意明确创建对象结构;它说我只是确定序列化可以采取的一些高级编程保健...


我不是想救一两行每类(通过移动from_xml行为变成初始化或类方法等)。我正在寻找重复我的心理过程的“meta”解决方案:

“我知道每个元素都将成为一个类名,我知道每个XML属性都将是一个字段名称,我知道要分配的代码只是@#{attribute_name} = el。[#{attribute_name}],然后递归到子元素中,并在to_xml上反转。


我同意建议“建设者”类加上XmlSimple似乎是正确的道路。 XML - >哈希 - >? - >对象模型(!和利润)


更新2008-09-18 AM:从@Roman,@fatgeekuk和@ScottKoon很好的建议似乎已经打破了问题打开。我下载了HPricot源代码,以了解它是如何解决问题的;关键方法显然是instance_variable_set和class_eval。 IRB的工作是非常鼓舞人心的,我现在正朝着实现....很兴奋

回答

0

你能定义一个方法缺少允许你做:

@bar = el.bar?这将摆脱一些样板。如果蝙蝠总是会被定义的方式,你可以推的XPath进入初始化方法,

class Bar 
    def initialize(el) 
    self.from_xml(XPath.first(el, "./bat")) 
    end 
end 

角度来说,Hpricot或REXML也会有所帮助。

1

你可以使用生成器,而不是创建to_xml方法,你可以使用XMLSimple拉你的XML文件转换成一个哈希,而不是使用从_xml方法。不幸的是,我不确定你真的会从使用这些技术中获得那么多。

0

我会继承attr_accessor来为你建立你的to_xml和from_xml。

像这样(注意,这是没有充分发挥作用,仅仅是概要)

class XmlFoo 
    def self.attr_accessor attributes = {} 
    # need to add code here to maintain a list of the fields for the subclass, to be used in to_xml and from_xml 
    attributes.each do |name, value| 
     super name 
    end 
    end 

    def to_xml options={} 
    # need to use the hash of elements, and determine how to handle them by whether they are .kind_of?(XmlFoo) 
    end 

    def from_xml el 
    end 
end 

,那么你可以使用它像....

class Second < XmlFoo 
    attr_accessor :first_attr => String, :second_attr => Float 
end 

class First < XmlFoo 
    attr_accessor :normal_attribute => String, :sub_element => Second 
end 

希望这给出了一个大致的了解。

1

我建议使用XmlSimple作为开始。在输入文件上运行XmlSimple#xml_in后,会得到一个散列。然后你可以递归到它(obj.instance_variables),并把所有的内部哈希(element.is_a(散)?)同名的对象,例如:

obj.instance_variables.find {|v| obj.send(v.gsub(/^@/,'').to_sym).is_a?(Hash)}.each do |h| 
    klass= eval(h.sub(/^@(.)/) { $1.upcase }) 

也许一个更清洁的方式,可以发现去做这个。 之后,如果你想从这个新对象中创建一个xml,你可能需要改变XmlSimple#xml_out来接受另一个选项,它将你的对象与它用来作为参数接收的通常散列区分开来,然后你必须编写XmlSimple#value_to_xml方法的版本,因此它将调用访问器方法,而不是尝试访问散列结构。另一种选择是让所有的类都通过返回想要的实例变量来支持[]运算符。