web-dev-qa-db-fra.com

Python objets sérialisables json

class gpagelet:
    """
    Holds   1) the pagelet xpath, which is a string
            2) the list of pagelet shingles, list
    """
    def __init__(self, parent):
        if not isinstance( parent, gwebpage):
            raise Exception("Parent must be an instance of gwebpage")
        self.parent = parent    # This must be a gwebpage instance
        self.xpath = None       # String
        self.visibleShingles = [] # list of tuples
        self.invisibleShingles = [] # list of tuples
        self.urls = [] # list of string

class gwebpage:
    """
    Holds all the datastructure after the results have been parsed
    holds:  1) lists of gpagelets
            2) loc, string, location of the file that represents it
    """
    def __init__(self, url):
        self.url = url              # Str
        self.netloc = False         # Str
        self.gpagelets = []         # gpagelets instance
        self.page_key = ""          # str

Existe-t-il un moyen de rendre ma classe json sérialisable? Ce qui m'inquiète, c'est la référence récursive.

30
asdfgaeg

Écrivez votre propre encodeur et décodeur, ce qui peut être très simple comme return __dict__

par exemple. voici un encodeur pour vider une arborescence totalement récursive, vous pouvez l'améliorer ou l'utiliser tel quel pour votre propre usage

import json

class Tree(object):
    def __init__(self, name, childTrees=None):
        self.name = name
        if childTrees is None:
            childTrees = []
        self.childTrees = childTrees

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if not isinstance(obj, Tree):
            return super(MyEncoder, self).default(obj)

        return obj.__dict__

c1 = Tree("c1")
c2 = Tree("c2") 
t = Tree("t",[c1,c2])

print json.dumps(t, cls=MyEncoder)

il imprime

{"childTrees": [{"childTrees": [], "name": "c1"}, {"childTrees": [], "name": "c2"}], "name": "t"}

vous pouvez également écrire un décodeur, mais là, vous devrez en quelque sorte identifier s'il s'agit de votre objet ou non, alors vous pouvez également mettre un type si nécessaire.

49
Anurag Uniyal

jsonpickle POUR LE GAGNER!

(Je viens de poser cette même question ... json pickle gère les graphes d'objets récursifs/imbriqués ainsi que les courts-circuits pour les graphes d'objets cycliques).

16
longda

Réponse indirecte: au lieu d'utiliser JSON, vous pouvez utiliser YAML , qui n'a aucun problème à faire ce que vous voulez. (JSON est essentiellement un sous-ensemble de YAML.)

Exemple:

import yaml
o1 = gwebpage("url")
o2 = gpagelet(o1)
o1.gpagelets = [o2]
print yaml.dump(o1)

En fait, YAML gère bien les références cycliques pour vous.

5
Eric O Lebigot

J'ai implémenté une méthode todict très simple à l'aide de https://stackoverflow.com/a/11637457/1766716

  • Itérer sur les propriétés qui ne commencent pas par __
  • Éliminer les méthodes
  • Éliminer manuellement certaines propriétés qui ne sont pas nécessaires (pour mon cas, provenant de sqlalcemy)

Et utilisé getattr pour construire le dictionnaire.

class User(Base):
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))
    password = Column(String(20))
    def props(self):
        return filter(
            lambda a:
            not a.startswith('__')
            and a not in ['_decl_class_registry', '_sa_instance_state', '_sa_class_manager', 'metadata']
            and not callable(getattr(self, a)),
            dir(self))
    def todict(self):
        return {k: self.__getattribute__(k) for k in self.props()}
3
guneysus

Ma solution pour cela était d'étendre la classe "dict" et d'effectuer des vérifications autour des attributs requis/autorisés en remplaçant les méthodes de classe init, update et set.

class StrictDict(dict):
    required=set()
    at_least_one_required=set()
    cannot_coexist=set()
    allowed=set()
    def __init__(self, iterable={}, **kwargs):
        super(StrictDict, self).__init__({})
        keys = set(iterable.keys()).union(set(kwargs.keys()))
        if not keys.issuperset(self.required):
            msg = str(self.__class__.__name__) + " requires: " + str([str(key) for key in self.required])
            raise AttributeError(msg)
        if len(list(self.at_least_one_required)) and len(list(keys.intersection(self.at_least_one_required))) < 1:
            msg = str(self.__class__.__name__) + " requires at least one: " + str([str(key) for key in self.at_least_one_required])
            raise AttributeError(msg)
        for key, val in iterable.iteritems():
            self.__setitem__(key, val)
        for key, val in kwargs.iteritems():
            self.__setitem__(key, val)

    def update(self, E=None, **F):
        for key, val in E.iteritems():
            self.__setitem__(key, val)
        for key, val in F.iteritems():
            self.__setitem__(key, val)
        super(StrictDict, self).update({})

    def __setitem__(self, key, value):
        all_allowed = self.allowed.union(self.required).union(self.at_least_one_required).union(self.cannot_coexist)
        if key not in list(all_allowed):
            msg = str(self.__class__.__name__) + " does not allow member '" + key + "'"
            raise AttributeError(msg)
        if key in list(self.cannot_coexist):
            for item in list(self.cannot_coexist):
                if key != item and item in self.keys():
                    msg = str(self.__class__.__name__) + "does not allow members '" + key + "' and '" + item + "' to coexist'"
                    raise AttributeError(msg)
        super(StrictDict, self).__setitem__(key, value)

Exemple d'utilisation:

class JSONDoc(StrictDict):
    """
    Class corresponding to JSON API top-level document structure
    http://jsonapi.org/format/#document-top-level
    """
    at_least_one_required={'data', 'errors', 'meta'}
    allowed={"jsonapi", "links", "included"}
    cannot_coexist={"data", "errors"}
    def __setitem__(self, key, value):
        if key == "included" and "data" not in self.keys():
            msg = str(self.__class__.__name__) + " does not allow 'included' member if 'data' member is not present"
            raise AttributeError(msg)
        super(JSONDoc, self).__setitem__(key, value)

json_doc = JSONDoc(
    data={
        "id": 5,
        "type": "movies"
    },
    links={
        "self": "http://url.com"
    }
)
2
g.carey