2012-09-19 10 views
1

Mes modèles ont tous une méthode qui convertit le modèle à un dictionnaire:Python StructuredProperty au dictionnaire

def to_dict(model): 
    output = {} 
    SIMPLE_TYPES = (int, long, float, bool, dict, basestring, list) 
    for key, prop in model._properties.iteritems(): 
     value = getattr(model, key) 

     if value is None: 
      continue 
     if isinstance(value, SIMPLE_TYPES): 
      output[key] = value 
     elif isinstance(value, datetime.date): 
      dateString = value.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] 
      output[key] = dateString 
     elif isinstance(value, ndb.Model): 
      output[key] = to_dict(value) 
     else: 
      raise ValueError('cannot encode ' + repr(prop)) 
    return output 

Maintenant, un de mes modèles, X, a une LocalStructuredProperty:

metaData = ndb.LocalStructuredProperty(MetaData, repeated=True) 

Ainsi, répété = True signifie qu'il s'agira d'une liste d'objets MetaData. MetaData est un autre modèle, et il a également la même méthode to_dict.

Cependant, quand je l'appelle json.dumps(xInstance.to_dict()), je reçois une exception:

raise TypeError(repr(o) + " is not JSON serializable") 
TypeError: MetaData(count=0, date=datetime.datetime(2012, 9, 19, 2, 46, 56, 660000), unique_id=u'8E2C3B07A06547C78AB00DD73B574B8C') is not JSON serializable 

Comment puis-je gérer cela?

+0

On ne sait pas comment votre définition est une méthode d'instance. On ne sait pas non plus ce que vous essayez de sérialiser. Si vous essayez de sérialiser la méthode 'X.to_dict' cela échouera certainement. – bossylobster

+0

Presque positifs, ils ont juste oublié les parenthèses lors de la publication de la question. –

+0

Désolé, il aurait dû être xInstance.to_dict() plutôt que X.to_dict ... mais c'était juste une faute de frappe sur SO, le problème existe toujours dans la vraie vie – Snowman

Répondre

2

Si vous voulez gérer cela dans to_dict() et avant le niveau de sérialisation à JSON, vous aurez juste besoin de quelques cas de plus dans votre to_dict(). Premièrement, vous avez dit que la définition to_dict ci-dessus est une méthode. Je l'aurais délégué à une fonction ou staticmethod ainsi vous avez quelque chose que vous pouvez appeler ints et tel sans vérifier le type d'abord. Le code sortira mieux de cette façon.

def coerce(value): 
    SIMPLE_TYPES = (int, long, float, bool, basestring) 
    if value is None or isinstance(value, SIMPLE_TYPES): 
     return value 
    elif isinstance(value, datetime.date): 
     return value.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] 
    elif hasattr(value, 'to_dict'): # hooray for duck typing! 
     return value.to_dict() 
    elif isinstance(value, dict): 
     return dict((coerce(k), coerce(v)) for (k, v) in value.items()) 
    elif hasattr(value, '__iter__'): # iterable, not string 
     return map(coerce, value) 
    else: 
     raise ValueError('cannot encode %r' % value) 

Branchez ensuite tout cela dans votre méthode elle-même to_dict:

def to_dict(model): 
    output = {} 
    for key, prop in model._properties.iteritems(): 
     value = coerce(getattr(model, key)) 
     if value is not None: 
      output[key] = value 
    return output 
+0

Comment 'elif hasattr (value, 'to_dict'):' fonctionne-t-il? Il vérifie s'il a une méthode nommée to_dict()? – Snowman

+0

Oui. Si c'est là, appelez-le. C'est une sorte d '"interface implicite" et beaucoup de choses en python fonctionnent de cette façon, mais peut-être qu'un nom un peu plus long aurait moins de chance de collisions s'il vous dérange. Ou vous pouvez juste le changer à la vérification isinstance. –

1

Tout ce que vous devez faire pour sérialiser est d'implémenter une fonction

def default_encode(obj): 
    return obj.to_dict() 

puis encoder votre JSON avec

json.dumps(X.to_dict(), default=default_encode) 
+0

S'il vous plaît voir mon dernier commentaire. Je ne sais pas si cela fait une différence, mais je ne suis pas sûr si vous comprenez ma question. Le problème est que lorsque la méthode to_dict() du modèle X rencontre une liste de méta-données, elle ne sérialise pas les objets de la liste, mais place simplement la liste dans le dictionnaire. Je veux que la méthode de l'instance X gère le cas où la propriété est une liste de méta-données, où dans ce cas, assurez-vous d'appeler to_dict() sur l'objet MetaData aussi ... avez-vous ce que je veux dire? – Snowman

0

je me suis dit comment résoudre le problème: dans la classe X, ajouter à la to_dict() méthode:

  ...   
      if value is None: 
       continue 
      if key == 'metaData': 
       array = list() 
       for data in value: 
        array.append(data.to_dict()) 
       output[key] = array 
      elif isinstance(value, SIMPLE_TYPES): 
       output[key] = value 
      ... 

Bien que je ne suis pas vraiment sûr de savoir comment automatiser ce cas où ce n'est pas Basé sur la clé, mais chaque fois qu'il rencontre une liste d'objets personnalisés, il convertit d'abord chaque objet dans la liste to_dict() en premier.

Questions connexes