7

给定一个中等复杂的 XML 结构(数十个元素,数百个属性),没有 XSD 并希望创建一个对象模型,避免编写样板 from_xml() 和 to_xml() 方法的优雅方法是什么?

例如,给定:

<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 行为移动到初始化程序或类方法等)。我正在寻找复制我的心理过程的“元”解决方案:

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


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


2008 年 9 月 18 日更新:@Roman、@fatgeekuk 和 @ScottKoon 的出色建议似乎已经解决了问题。我下载了 HPricot 源代码,看看它是如何解决问题的;关键方法显然是 instance_variable_set 和 class_eval 。irb 的工作非常令人鼓舞,现在正在走向实施....非常兴奋

4

5 回答 5

1

您可以使用 Builder 而不是创建您的 to_xml 方法,并且您可以使用 XMLSimple 将您的 xml 文件拉入 Hash 而不是使用 from _xml 方法。不幸的是,我不确定您是否真的会从使用这些技术中获得这么多。

于 2008-09-17T20:12:58.473 回答
1

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

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 方法版本,因此它将调用访问器方法而不是尝试访问哈希结构。另一种选择是让所有类通过返回所需的实例变量来支持 [] 运算符。

于 2008-09-18T12:25:09.450 回答
0

您能否定义一个缺少的方法来允许您执行以下操作:

@bar = el.bar?那将摆脱一些样板。如果 Bat 总是以这种方式定义,您可以将 XPath 推送到初始化方法中,

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

Hpricot 或 REXML 也可能有帮助。

于 2008-09-17T20:07:22.340 回答
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

希望这能提供一个总体思路。

于 2008-09-18T11:08:35.030 回答
0

你能尝试用 hpricot 解析 XML并使用输出来构建一个普通的旧 Ruby 对象吗?[免责声明]我没有试过这个。

于 2008-09-18T05:46:03.527 回答