Рубиновый код для быстрой и грязной сериализации XML? - PullRequest
7 голосов
/ 18 сентября 2008

Учитывая относительно сложную структуру 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. [# {имя_атрибута}], а затем выполнить рекурсию в подэлементы. И вернуться к to_xml. "


Я согласен с предположением, что класс "builder" плюс XmlSimple кажется правильным путем. XML -> Hash ->? -> Объектная модель (и Прибыль!)


Обновление 2008-09-18 AM: Прекрасные предложения от @Roman, @fatgeekuk и @ScottKoon, похоже, открыли проблему. Я скачал исходник HPricot, чтобы посмотреть, как он решил проблему; Ключевые методы - это instance_variable_set и class_eval. Работа IRB очень воодушевляет, сейчас я двигаюсь к реализации ... Очень взволнован

Ответы [ 5 ]

1 голос
/ 18 сентября 2008

Я предлагаю использовать 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, чтобы он вызывал метод доступа, а не пытался получить доступ к хеш-структуре. Другой вариант - заставить все ваши классы поддерживать оператор [], возвращая требуемую переменную экземпляра.

1 голос
/ 18 сентября 2008

Вы можете использовать Builder вместо создания своего метода to_xml, и вы можете использовать XMLSimple для перетаскивания вашего xml-файла в Hash вместо использования метода from _xml. К сожалению, я не уверен, что вы действительно сильно выиграете от использования этих методов.

0 голосов
/ 18 сентября 2008

Я бы создал подкласс 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

Надеюсь, это дает общее представление.

0 голосов
/ 18 сентября 2008

Не могли бы вы попытаться проанализировать XML с помощью hpricot и использовать выходные данные для создания простого старого объекта Ruby? [ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ] Я не пробовал это.

0 голосов
/ 18 сентября 2008

Не могли бы вы определить метод, который позволяет вам сделать:

@ bar = el.bar? Это избавило бы от некоторого шаблона. Если Bat всегда будет определяться таким образом, вы можете вставить XPath в метод initialize,

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

Hpricot или REXML тоже могут помочь.

...