IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

XML et objets


Sujet :

Python

  1. #1
    Membre averti
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Mai 2003
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Mai 2003
    Messages : 48
    Par défaut XML et objets
    Bonjour,

    Je me suis mis à Python il n'y a pas si longtemps donc je suis loin d'en maîtriser la philosophie. Je crois que le coeur de mon problème vient là, je ne pense pas bien "Python".
    En fait, je souhaite écrire une classe qui permet de gérer les aspects XML pour écrire/lire un objet en XML sans redéfinir à chaque fois la méthode.
    On définit dans chaque classe un genre de dictionnaire qui fait la correspondance entre attribut d'une balise XML et la variable où sera stocké cette valeur, etc ....
    Le problème c'est qu'un dictionnaire stocke des valeurs et pas les adresses des variables elles même. Voici un petit exemple de ce que je souhaite faire :

    exemple de fichier XML :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    <db>
    <object arg1="2" arg2="56"> ....</object>
    <object arg1="4" arg2="89"> ....</object>
    </db>
    Un genre de pseudo code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    import xml.etree.ElementTree as etree
    class MyXML:
       def __init__(self):
          .....
     
       def read(self, dic):
          # <object var1="2" var2="56">
          # selon le dictionnaire :
          # la valeur de var1 (2) doit être stockée dans object1.var1
          # la valeur de var2 (56) doit être stockée dans object1.var2
     
       def write()
          ......
     
    class objectA:
       def __init__(self):
          self.var1 = None
          self.var2 = None
     
       def read(self, myxml):
          myxml.read(self,  {"arg1" : self.var1, "arg2" : self.var2})
     
    if __name__ == "__main__":
       myxml = MyXML()
       object1 = objectA()
       object1.read()
       print(object1.var1)
       print(object1.var2)
    Affichage console :
    Je pense que je dois pas utiliser la bonne méthode... J'avoue ne pas savoir comment prendre le problème proprement.

    Si quelqu'un a une idée, je suis preneur.

    D'avance merci

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 715
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 715
    Par défaut
    Les éléments d'une structure XML sont généralement des 'arbres', i.e. des boîtes qui ont éventuellement des attributs mais qui contiennent aussi d'autres éléments.

    Dans le cas général, il n'y a pas de correspondance 'simple' entre n'importe quel arbre XML et une arborescence de classes Python: on peut s'en sortir avec quelques règles dans le sens Python => XML mais il va falloir guider la traduction XML => Python => XML pour qu'on retrouve ses petits à l'arrivée.

    Si vous vous contentez de:
    <db>
    <object arg1="2" arg2="56"> ....</object>
    <object arg1="4" arg2="89"> ....</object>
    </db>
    vous dites mes éléments XML sont des listes d'empty elements.
    Un empty element étant un élément qui n'a que des attributs.
    C'est une simplification intéressante car elle vous permet de représenter le contenu d'une table de BDD et la structure équivalente Python est un dict ou
    une classe ayant les mêmes attributs.

    Après la question est j'ai un attribut nommé 'arg1' de la représentation XML d'objet-1.
    Son 'adresse' dans la représentation lxml est de la forme e.attrib['arg1'] ou e est l'element lxml correspondant...

    Dans ce cas, la 'classe' Python doit être associée à e et les accès à 'arg1' traduit en e.attrib['arg1'] - dit autrement la classe Python doit être un "Proxy" vers la chose lxml.

    Voilà pour les principes.
    La mise en oeuvre est donnée ci dessous.

    Attention, c'est du Python. Si vous débutez vous allez trouver cela un peu magique : ralez, demandez des explications,...

    Mais n'oubliez pas qu'apprendre demande du temps et que mon code intègre tellement de choses que vous n'avez peut être eu que le temps de survoler que vous risquez d'être déboussolé.
    Courage, c'est juste le signe que vous avez encore pleins de choses à découvrir!

    - W



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    from lxml import etree
     
    xml = '''\
    <db>
    <object arg1="2" arg2="56"> </object>
    <object arg1="4" arg2="89"> </object>
    </db>'''
     
    class XMLProxy(object):
     
        def __init__(self, element):
            object.__init__(self)
            object.__setattr__(self, '_element', element)
     
        def __getattr__(self, key):
            return self._element.get(key)
     
        def __setattr__(self, key, value):
            return self._element.set(key, value)
     
        def __str__(self):
            return str(self._element.attrib)
     
    class MyXML(object):
     
        def __init__(self, xml):
            self._xml = xml
            self._root = etree.XML(xml)
     
        def get_elements(self):
            for e in self._root:
                yield XMLProxy(e)
     
        def dumps(self):
            return etree.tostring(self._root, pretty_print=True)
     
    myxml = MyXML(xml)
    L = []
    for e in myxml.get_elements():
        L.append(e)
    e = L[0]
    print e
    e.arg1 = 'XXX'
    print e
    e.tutu = 'ZZZ'
    print e
     
    print myxml.dumps()
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre averti
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Mai 2003
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Mai 2003
    Messages : 48
    Par défaut
    Merci wiztricks !

    Effectivement, il y a certaines choses (lxml, yield, __setattr__ et aussi étendre la classe object) que je ne connais pas mais c'est ce qui rend la chose intéressante.

    Je vais essayer d'assimiler tout ça. Si j'ai des questions, je viendrai les poser ici même

    Encore merci

  4. #4
    Membre averti
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Mai 2003
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Mai 2003
    Messages : 48
    Par défaut
    Je reviens sur ce post pour vous proposer mes modifs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    #!/usr/bin/python2.6
    # -*- coding: utf-8 -*-
    from lxml import etree
    import types
     
    xml_string = '''<?xml version='1.0' encoding='UTF-8'?>
    <backups_db>
      <backup id='1' name='Photos'>
        <location type="file">/home/user/maphoto.jpg</location>
        <location type="dir">/home/user/Photos</location>
      </backup>
      <backup id='2' name='Vidéos'>
        <location type="file">/home/user/a\ trier/vacances.mkv</location>
        <location type="dir">/home/user/Vidéos</location>
        <location type="file">/home/user/montage.ogm</location>
      </backup>
    </backups_db>'''
     
    class XMLProxy(object):
        def __init__(self, file):
            object.__init__(self)
            self.file = file
            self.root = etree.XML(self.file)
     
        def read_xml(self):
            return self.__read_xml(self.root)
     
        def __read_xml(self, root):
            obj = dic_str_to_class[root.tag.capitalize()]()
            for attribute in root.attrib:
                obj.__setattr__(attribute.capitalize(), root.attrib[attribute])
            if(len(root) == 0):
                obj.__setattr__("Value", root.text)
            else:
                children = []
                for child in root:
                    children.append(self.__read_xml(child))
                if(len(children) > 0):
                    obj.__setattr__(children[0].__class__.__name__.capitalize()+"s", children)
            return obj
     
        def write(self, obj, output_file):
            elt = self.__write(obj)
            elt_tree = etree.ElementTree(elt)
            return elt
     
        def __write(self, obj):
            tag = obj.__class__.__name__.lower()
            attributes = {}
            children = None
            value = None
            for name in self.get_xml_variables(obj):
                if(isinstance(obj.__getattribute__(name), types.ListType)):
                    children = obj.__getattribute__(name)
                else:
                    attributes[name.lower()] = obj.__getattribute__(name) 
            if(attributes.has_key("value")):
                value = attributes["value"]
                attributes.__delitem__("value")
            if(value != None and children != None):
                print("Erreur XMLProxy.write() : value et children != None")
                sys.exit(2)
            elt = etree.Element(tag, attributes)
     
            if(value != None):
                elt.text = value
            elif(children != None):
                for child in children:
                    elt.append(self.__write(child))
            return elt 
     
        def get_xml_variables(self, obj):
            for var in dir(obj):
                if(var[0] != '_' and var == var.capitalize()):
                    yield var
     
     
    class Backups_db(object):
        def __init__(self):
            object.__init__(self)
     
    class Backup(object):
        def __init__(self):
            object.__init__(self)
     
    class Location(object):
        def __init__(self):
            object.__init__(self)
     
    dic_str_to_class = { "Backups_db" : Backups_db,"Backup": Backup, "Location" : Location }
     
    if __name__ == "__main__":
        xmlproxy = XMLProxy(xml_string)
        obj = xmlproxy.read_xml()
        xml_element = xmlproxy.write(obj, "file2.xml")
        print(etree.tostring(xml_element, pretty_print=True, encoding=unicode))
    Le but du script est vraiment de ne pas gérer l'aspect XML. Je m'explique :
    En gros, la classe XMLProxy fait tout, on appelle la methode read sur un fichier XML et Hop ! on se retrouve avec tous nous objets instanciés, prêts à être utilisés.
    Cependant, il y a plusieurs conditions à respecter pour que ça fonctionne :
    - pour un élément XML "<test id=1>texte bla bla ...</test>", il faut créer une classe "Test". l'attribut XML id deviendra une variable "Id". La majuscule permet de distinguer ce qui est XML de ce qui ne l'est pas.
    - si un élément XML ne contient pas de sous élément mais uniquement du texte, une variable "Value" sera créé.
    - si un élément XML contient des sous éléments, une liste sera créée. Son nom sera le même que ne nom de l'element XML avec la fameuse majuscule.
    J'espère avoir été clair

    Y a deux petites choses que j'aurai aimé améliorer mais je ne vois pas comment faire :
    - faire en sorte de pas devoir créer le dictionnaire : dic_str_to_class
    - la méthode write ne crée pas "<?xml version='1.0' encoding='UTF-8'?>"

    Que pensez-vous de ma manière d'aborder le problème ?
    Est-ce "propre" ?
    Est-ce que le code est propre dans le sens "langage python" ?

    D'avance merci !

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 715
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 715
    Par défaut
    Salut
    Citation Envoyé par sluke Voir le message
    - la méthode write ne crée pas "<?xml version='1.0' encoding='UTF-8'?>"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        print(etree.tostring(xml_element, pretty_print=True, encoding="utf-8", xml_declaration=True))
    Pour le reste, il faut que je regarde un peu çà
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 715
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 715
    Par défaut
    Hypothèses de travail.

    -- un peu plus forte que les votres --
    1. La structure d'éléments XML est grossièrement un arbre - Tree -,
    2. On souhaite (enfin peut être) prendre un arbre sérialisé XML (votre xml_string) et l'associer à une instance d'un des arbres conformes aux arbres "possibles" décrits par le XSD - par exemple.
    3. On souhaite pouvoir naviguer dans l'instance ou les arbres "possibles" via des "dotted names" i.e x.y.z...

    Oublions le cas général et contentons nous de (3).

    Citation Envoyé par sluke Voir le message
    Cependant, il y a plusieurs conditions à respecter pour que ça fonctionne :
    1- pour un élément XML "<test id=1>texte bla bla ...</test>", il faut créer une classe "Test". l'attribut XML id deviendra une variable "Id". La majuscule permet de distinguer ce qui est XML de ce qui ne l'est pas.
    2- si un élément XML ne contient pas de sous élément mais uniquement du texte, une variable "Value" sera créé.
    3- si un élément XML contient des sous éléments, une liste sera créée. Son nom sera le même que ne nom de l'element XML avec la fameuse majuscule.
    Un élément XML est un nœud N de notre arbre.
    Les attributs doivent être accessibles comme tout attribut d'une classe Python: si l'attribut est nommé 'attr', on y accède via n.att
    La majuscule permet de distinguer ce qui est XML de ce qui ne l'est pas.
    Pour l'instant, on ne sait pas trop comment on va pouvoir mettre autre chose que ce qui sort du "string" XML alors je ne vois pas trop mais:
    1 - si on a une class qui définit "att" est qu'"att" est un attribut XML quelle que soit la convention de nommage, il y aura toujours des collisions "possibles" => plutôt que de "renommer", il est plus simple d'interdire que ce soit autre chose via une "property"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class A(object):
        def __init__(self, value):
             self.att = value
        def set_att(self, value):
             self._att = value
        def get_att(self):
             return self._att
        att = property(fset=set_att, fget=get_att)
    Pour (3), un élément peut contenir les occurences d'un ou de plusieurs éléments. i.e Dans N, on pourra avoir plusieurs A, 1 B,...
    La forme la plus appropriée me semble être un dict dont les clés sont le nom des éléments et les valeurs des types simples, ou des types complexes ou des listes de...
    Après la question est comment attraper n.element_name en le traitant à coup de property mais on a encore des collisions à gérer et des 'règles' à définir...
    => je ne vois pas trop comment s'en sortir sans avoir définit "attributs" et "elements"
    Tout çà pour dire que le cas général n'est pas "simple".
    Mais c'est une bonne discussion
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre averti
    Homme Profil pro
    Administrateur systèmes et réseaux
    Inscrit en
    Mai 2003
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Administrateur systèmes et réseaux

    Informations forums :
    Inscription : Mai 2003
    Messages : 48
    Par défaut
    Il est clair que ce genre de système souffrira toujours de limitations mais c'est bien pratique pour des utilisations assez simples du langage XML.

    Merci pour votre aide.

    A bientôt !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 5
    Dernier message: 14/07/2008, 15h12
  2. [Axis] Désérialisation XML vers Objet Java
    Par jemini_fr dans le forum Services Web
    Réponses: 2
    Dernier message: 10/12/2007, 14h38
  3. web.xml, sérialisation objet
    Par ep31 dans le forum Tomcat et TomEE
    Réponses: 1
    Dernier message: 26/11/2007, 10h46
  4. XML - Créer objet
    Par lamoua76 dans le forum Format d'échange (XML, JSON...)
    Réponses: 3
    Dernier message: 16/11/2007, 14h18
  5. Serialisation Xml d'objets : et les types particuliers ?
    Par zax-tfh dans le forum Framework .NET
    Réponses: 4
    Dernier message: 27/07/2007, 14h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo