2008-09-17 12 views
7

Etant donné une structure XML modérément complexe (des dizaines d'éléments, des centaines d'attributs) sans XSD et une volonté de créer un modèle objet, comment éviter d'écrire des méthodes de type xml() et to_xml()?Ruby code pour sérialisation XML rapide et sale?

Par exemple, étant donné:

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

Comment puis-je éviter d'écrire des séquences infinies de:

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... 

Cela ne me dérange pas de créer la structure de l'objet explicitement; il est la sérialisation que je suis sûr que peut être pris en charge avec une programmation de niveau supérieur ...


Je ne cherche pas à enregistrer une ligne ou deux par classe (en déplaçant le comportement from_xml dans initialiseur ou méthode de classe, etc.). Je cherche la solution "méta" qui duplique mon processus mental:

"Je sais que chaque élément va devenir un nom de classe, je sais que chaque attribut XML va être un nom de champ. le code à affecter est simplement @ # {nom_attribut} = el. [# {nom_attribut}], puis se recurcit en sous-éléments et inverser sur to_xml. " Je suis d'accord avec la suggestion qu'une classe "Builder" plus XmlSimple semble être le bon chemin. XML -> Hash ->? -> Modèle d'objet (! Et Profit)


Mise à jour 2008-09-18 AM: d'excellentes suggestions @Roman, @fatgeekuk et @ScottKoon semblent avoir rompu le problème ouvert. J'ai téléchargé HPricot source pour voir comment il a résolu le problème; Les méthodes clés sont clairement instance_variable_set et class_eval. le travail est irb très encourageant, je suis maintenant à la mise en œuvre .... en mouvement très excité

Répondre

0

Pourriez-vous définir une méthode manquante qui vous permet de le faire:

@bar = el.bar? Cela permettrait de se débarrasser de certains passe-partout. Si Bat va toujours à définir cette façon, vous pouvez pousser le XPath dans la méthode initialize,

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

hpricot ou REXML peut aider aussi.

1

Vous pouvez utiliser Builder au lieu de créer votre méthode to_xml, et vous pouvez utiliser XMLSimple pour extraire votre fichier xml dans un Hash au lieu d'utiliser la méthode from _xml. Malheureusement, je ne suis pas sûr que vous aurez vraiment beaucoup à gagner en utilisant ces techniques.

0

Je sous-classerais attr_accessor pour construire vos to_xml et from_xml pour vous.

Quelque chose comme ceci (note, ce n'est pas entièrement fonctionnel, seul un aperçu)

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 

vous pouvez alors l'utiliser comme ....

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

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

Hope this donne une idée générale.

1

Je suggère d'utiliser un XmlSimple pour commencer. Après avoir exécuté XmlSimple # xml_in dans votre fichier d'entrée, vous obtenez un hachage. Ensuite, vous pouvez récursif en elle (obj.instance_variables) et tourner tous les hash internes (element.is_a (Hash)?) Aux objets du même nom, par exemple:

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

Peut-être un moyen plus propre peut être trouvé pour faire ça. Ensuite, si vous voulez faire un XML à partir de ce nouvel objet, vous devrez probablement changer le XmlSimple # xml_out pour accepter une autre option, ce qui distingue votre objet du hash habituel utilisé comme argument, et vous Je vais devoir écrire votre version de la méthode XmlSimple # value_to_xml, donc elle appellera la méthode accessor au lieu d'essayer d'accéder à une structure de hachage. Une autre option consiste à demander à toutes les classes de prendre en charge l'opérateur [] en retournant la variable d'instance voulue.