2008-09-17 90 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方法的版本,因此它將調用訪問器方法,而不是嘗試訪問散列結構。另一種選擇是讓所有的類都通過返回想要的實例變量來支持[]運算符。