Dicas para lidar com JSON

Você já deve ter descoberto como funciona o formato JSON — muito usado para trocar informações entre aplicações Web, como já foi mostrado aqui no blog anteriormente. Hoje vamos mostrar algumas dicas para facilitar sua vida quando estiver lidando com esse formato.

1) Use python -mjson.tool para formatar saídas JSON na linha de comando.

Às vezes cai no nosso colo um conteúdo JSON não-formatado, com todo o conteúdo em uma linha só, algo parecido com isso:

{"assunto":"Dicas para lidar com JSON","metadados":{"data":"07/07/2013 14:10","site":"https://pythonhelp.wordpress.com","numero_acessos":3},"conteudo":"Voc\u00ea j\u00e1 deve ter descoberto como funciona o formato JSON -- muito usado para trocar informa\u00e7\u00f5es entre aplica\u00e7\u00f5es Web..."}

Geralmente, trata-se da resposta de uma API, que é “limpado” para economizar alguns bytes e por conseguinte, reduzir o uso de banda do servidor. Até aí tudo bem, o problema é que fica bem mais complicado de ver a estrutura dos dados retornados. Fear not! O módulo json da API padrão de Python contém uma ferramenta para você formatar os resultados diretamente na linha de comando. Se o conteúdo acima estiver dentro de um arquivo com o nome post.json, você pode fazer:

$ python -m json.tool post.json
{
    "assunto": "Dicas para lidar com JSON",
    "conteudo": "Voc\u00ea j\u00e1 deve ter descoberto como funciona o formato JSON -- muito usado para trocar informa\u00e7\u00f5es entre aplica\u00e7\u00f5es Web...",
    "metadados": {
        "data": "07/07/2013 14:10",
        "numero_acessos": 3,
        "site": "https://pythonhelp.wordpress.com"
    }
}

That’s cool, right?

Se você é que nem eu, provavelmente vai querer colocar um alias (apelido ou atalho) no ~/.bashrc, para ficar ainda mais fácil:

$ echo "alias jsonfmt='python -mjson.tool'" >> ~/.bashrc
$ source ~/.bashrc
$ echo "[1, 2, 3]" | jsonfmt
[
    1,
    2,
    3
]
$ curl -s http://api.joind.in | jsonfmt
{
    "events": "http://api.joind.in/v2.1/events",
    "hot-events": "http://api.joind.in/v2.1/events?filter=hot",
    "open-cfps": "http://api.joind.in/v2.1/events?filter=cfp",
    "past-events": "http://api.joind.in/v2.1/events?filter=past",
    "upcoming-events": "http://api.joind.in/v2.1/events?filter=upcoming"
}

2) Estenda json.JSONEncoder para converter objetos em JSON:

Como você já sabe, é fácil converter dicionários Python no formato JSON. Mas e no caso de variáveis de classes que você mesmo definiu?

Observe esse exemplo:

import json, datetime

class BlogPost:
    def __init__(self, titulo):
        self.titulo = titulo
        self.data = datetime.datetime.now()

post = BlogPost('Dicas para lidar com JSON')

Se tentarmos fazer:

print json.dumps(post)

obtemos um erro parecido com:

TypeError: <__main__.BlogPost instance at 0x1370ab8> is not JSON serializable

Isto é porque o método json.dumps não sabe converter objetos do tipo BlogPost em strings no formato JSON. Felizmente, o método json.dumps permite que você informe um “encodificador” JSON alternativo, de forma que você pode customizar a geração do resultado JSON para permitir a conversão de outros objetos:

class BlogPostEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, BlogPost):
            return {'titulo': obj.titulo, 'data': obj.data}
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return json.JSONEncoder.default(self, obj)

print json.dumps(post, cls=BlogPostEncoder)

Agora sim, funciona:

{"titulo": "Dicas para lidar com JSON", "data": "2013-07-07T16:26:40.636950"}

Minha sugestão é usar um encodificador mais genérico, que permita converter em JSON qualquer objeto que implemente um método to_json, segue um exemplo completo:

import json, datetime

class Site:
    def __init__(self, url):
        self.url = url
    def to_json(self):
        return {"url": self.url}

class BlogPost:
    def __init__(self, titulo, site):
        self.titulo = titulo
        self.data = datetime.datetime.now()
        self.site = site
    def to_json(self):
        return {"titulo": self.titulo, "data": self.data.isoformat(), "site": self.site}

class GenericJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, 'to_json'):
            return obj.to_json()
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        return json.JSONEncoder.default(self, obj)

print json.dumps(Site("http://www.google.com.br"), cls=GenericJsonEncoder)
post = BlogPost('Dicas para lidar com JSON', Site('https://pythonhelp.wordpress.com'))

print json.dumps(post, cls=GenericJsonEncoder)

Note que você não precisa de nada disso se estiver usando o framework Django e tentando serializar uma instância de um modelo. Nesse caso, basta usar o mecanismo de serialização para XML e JSON do próprio Django.

3) Para necessidades mais complexas de serialização, mude a estratégia

Se você precisar converter objetos em JSON e vice-versa para coisas mais complicadas que o nosso exemplo, você pode considerar usar o módulo jsonpickle. Este módulo consegue converter quase qualquer objeto Python em JSON e vice-versa, usando uma representação própria baseada no módulo pickle para serialização de objetos. Essa representação própria acaba tendo algumas desvantagens, porque nem sempre o JSON gerado fica legível ou facilmente tratado no caso de outras linguagens, a portabilidade é garantida praticamente só para outras aplicações Python.

Por isso, se a serialização e deserialização de objetos complicados for um ponto muito importante para o seu projeto, considere usar outros formatos — JSON pode não ser o formato mais adequado. (UPDATE: aqui tem uma visão geral sobre alternativas de serialização em Python).

Anúncios

Um comentário sobre “Dicas para lidar com JSON

  1. Pingback: Serialização de Objetos em Python | Python Help

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s