Serialize Object to JSON

Asked

Viewed 1,864 times

8

How do I convert an object to JSON?

I even found some codes on the internet that even worked, but my object has some fields that are other objects and so the conversion did not reach the last level.

Example:

class Pessoa(object):
    codigo = 0
    nome = ''
    endereco = Endereco()

class Endereco(object):
    codigo = 0
    logradouro = ''
    bairro = ''
    cidade = Cidade()

class Cidade(object):
    codigo = 0
    nome = ''
    uf = ''

These are my objects, but doing the conversion I found, the result JSON comes only with the values contained in Person in Addressee, then stay City outside the JSON:

{
    'codigo': 1,
    'nome': 'Oliveira',
    'endereco': {
        'codigo': 5,
        'logradouro': 'Rua A',
        'bairro': 'Campos'            
    }
}

I mean, I believe the code didn’t go through all the levels.

I’m using the pyMongo and I saw that it has a library bson that has some conversions, but I did not know how to use it or else it does not do what I need.

  • I’m kind of confused - what are these fields defined in the class? You know that when you define fields like this, they’re static, right? To define object fields, you do inside the __init__. Maybe that’s why the object/json conversion methods you experienced aren’t giving you the expected result.

2 answers

5

First, you need to define your fields within the __init__, and not so in the class, otherwise they will be shared by all instances of the class (i.e. they will be "static") instead of having one for each object:

class Pessoa(object):
    def __init__(self):
        self.codigo = 0
        self.nome = ''
        self.endereco = Endereco()

class Endereco(object):
    def __init__(self):
        self.codigo = 0
        self.logradouro = ''
        self.bairro = ''
        self.cidade = Cidade()

class Cidade(object):
    def __init__(self):
        self.codigo = 0
        self.nome = ''
        self.uf = ''

(can put parameters - optional or mandatory - in the constructor if you want, instead of starting everything with a default value; just be careful with the default values that are also objects, putting it in the method signature will bring unwanted effects)

Many times you don’t accurate class - use literals for dict, or raise them by hand (such as suggested by Christian Felipe) might be enough. However, classes can be useful if you want to give them methods, so I don’t say don’t use, yes if necessary/convenient.

Once this is done, the object/json conversion libraries you experienced should give the correct result. But if you want a simple method to do by hand, you can take advantage of the fact that the field __dict__ of an object transforms its properties into fields of a dict:

import json

def para_dict(obj):
    # Se for um objeto, transforma num dict
    if hasattr(obj, '__dict__'):
        obj = obj.__dict__

    # Se for um dict, lê chaves e valores; converte valores
    if isinstance(obj, dict):
        return { k:para_dict(v) for k,v in obj.items() }
    # Se for uma lista ou tupla, lê elementos; também converte
    elif isinstance(obj, list) or isinstance(obj, tuple):
        return [para_dict(e) for e in obj]
    # Se for qualquer outra coisa, usa sem conversão
    else: 
        return obj

p = Pessoa()
s = json.dumps(para_dict(p))

This should work for the most common cases, and the use of json.dumps ensures that at least nothing invalid will be echoed to the output.

  • Thanks for your help, man!

3


So man, in Python there is the type of data Dictionary which is kind of the Map of other languages, is a structure of (key, value), that is, almost similar to the problem you are needing, because the format Json follows this pattern of (key, value).
Below is an example of a use of this structure that already comes in the language and does not need other packages and modules, but you must adapt to its use there, obviously:

Pessoa = {}
Endereco = {}
Cidade = {}

Endereco['Logradouro'] = "Rua A"
Endereco['Numero'] = 80
Endereco['Bairro'] = "Centro"

Cidade['Nome'] = "Belo Horizonte"
Cidade['UF'] = "Minas Gerais"
Cidade['Codigo'] = 0

Pessoa['Nome'] = "Meu nome é esse"
Pessoa['Codigo'] = 1
Pessoa['Endereco'] = Endereco
Pessoa['Cidade'] = Cidade

print(repr(Pessoa))

Upshot

{
    "Nome": "Meu nome é esse",
    "Codigo": 1,
    "Endereco": {
        "Numero": 80,
        "Logradouro": "Rua A",
        "Bairro": "Centro"
     },
    "Cidade": {
        "Nome": "Belo Horizonte",
        "Codigo": 0,
        "UF": "Minas Gerais"
     }
}

Then you can make a class that assembles these dictionaries separately, using the methods of access to their objects, and, with the method repr(obj) that you already return the json that you need, is solved your problem.

  • 1

    Are you sure the result of repr is always valid json? If you already have a structure with only dicts, listand primitive, can use import json and s = json.dumps(obj) to produce a valid json - and without the risk of serializing things that are not valid, such as functions. Such as the module json is native to Python, no need for external libraries.

  • A counterexample: >>> repr({'teste com aspas':'"'}) exit: '{\'teste com aspas\': \'"\'}'. Using json.dumps: '{"teste com aspas": "\\""}'

  • But in these cases of yours, you’re already playing one str for the method repr and not a dict as suggested for him to go riding. The point is, from what I’ve seen around, the structure dict implements a default method for all objects that is the obj.__repr__(), that there when I call the repr(obj) internally is called this method, which picks up the pairs (key, value) and plays them like json. In your case presented are str's and then the reproduction is really different. However, the form I presented is only one of the forms, using the module json is also a good.

  • I passed a dict pro repr, not a string. Anyway, to use this method one has to take care that the object does not have functions, tuples, dates... I’m not saying you can’t use it, I just find the json safer/more robust.

  • Well, on that I agree!

  • Thanks for your help, man!

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.