Eu tenho um Python set
que contém objetos __hash__
e __eq__
métodos para garantir que não haja duplicatas incluídas na coleção.
Eu preciso json codificar esse resultado set
, mas passar mesmo um vazio set
para o json.dumps
método gera a TypeError
.
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable
Eu sei que posso criar uma extensão para a json.JSONEncoder
classe que possui um default
método personalizado , mas nem sei por onde começar a conversão pelo set
. Devo criar um dicionário com os set
valores dentro do método padrão e depois retornar a codificação? Idealmente, gostaria de tornar o método padrão capaz de lidar com todos os tipos de dados que o codificador original usa (estou usando o Mongo como fonte de dados, para que as datas também pareçam causar esse erro)
Qualquer dica na direção certa seria apreciada.
EDITAR:
Obrigado pela resposta! Talvez eu devesse ter sido mais preciso.
Utilizei (e votei) as respostas aqui para contornar as limitações da set
tradução, mas há chaves internas que também são um problema.
Os objetos no set
são objetos complexos aos quais se traduzem __dict__
, mas eles também podem conter valores para suas propriedades que podem ser inelegíveis para os tipos básicos no codificador json.
Existem muitos tipos diferentes para isso set
, e o hash calcula basicamente um ID exclusivo para a entidade, mas no verdadeiro espírito do NoSQL não há como dizer exatamente o que o objeto filho contém.
Um objeto pode conter um valor de data para starts
, enquanto outro pode ter outro esquema que não inclui chaves que contenham objetos "não primitivos".
É por isso que a única solução em que consegui pensar foi estender o método JSONEncoder
para substituir o default
método para ativar casos diferentes - mas não sei ao certo como proceder e a documentação é ambígua. Em objetos aninhados, o valor retornado de default
passa por chave ou é apenas uma inclusão / descarte genérica que examina o objeto inteiro? Como esse método acomoda valores aninhados? Examinei as perguntas anteriores e não consigo encontrar a melhor abordagem para a codificação específica de caso (que infelizmente parece ser o que vou precisar fazer aqui).
fonte
dict
s? Eu acho que você quer fazer apenas umlist
fora do set e, em seguida, passá-lo para o codificador ... por exemplo:encode(list(myset))
Respostas:
A notação JSON possui apenas alguns tipos de dados nativos (objetos, matrizes, seqüências de caracteres, números, booleanos e nulos); portanto, qualquer coisa serializada no JSON precisa ser expressa como um desses tipos.
Conforme mostrado nos documentos do módulo json , essa conversão pode ser feita automaticamente por JSONEncoder e JSONDecoder , mas você desistiria de outra estrutura necessária (se converter conjuntos em uma lista, perderá a capacidade de recuperar regularmente se você converter conjuntos para um dicionário usando
dict.fromkeys(s)
, perderá a capacidade de recuperar dicionários).Uma solução mais sofisticada é criar um tipo personalizado que possa coexistir com outros tipos JSON nativos. Isso permite que você armazene estruturas aninhadas que incluem listas, conjuntos, dicts, decimais, objetos de data e hora, etc .:
Aqui está uma sessão de amostra mostrando que ele pode lidar com listas, dictos e conjuntos:
Como alternativa, pode ser útil usar uma técnica de serialização de uso mais geral, como YAML , Twisted Jelly ou módulo de pickle do Python . Cada um deles suporta uma variedade muito maior de tipos de dados.
fonte
JSONDecoder
faz, mas não usá-loVocê pode criar um codificador personalizado que retorne a
list
quando encontrar aset
. Aqui está um exemplo:Você também pode detectar outros tipos dessa maneira. Se você precisar manter a lista como um conjunto, poderá usar uma codificação personalizada. Algo como
return {'type':'set', 'list':list(obj)}
pode funcionar.Para ilustrar tipos aninhados, considere serializar isso:
Isso gera o seguinte erro:
Isso indica que o codificador receberá o
list
resultado retornado e chamará recursivamente o serializador em seus filhos. Para adicionar um serializador personalizado para vários tipos, você pode fazer isso:fonte
default
função será chamada novamente, desta vezobj
sendo um objeto de data, então você só precisa testá-lo e retornar uma representação de data.I adaptado solução de Raymond Hettinger para pitão 3.
Aqui está o que mudou:
unicode
desaparecidodefault
comsuper()
base64
para serializar obytes
tipo emstr
(porque parece quebytes
no python 3 não pode ser convertido em JSON)fonte
json.dumps()
retorna de / para'latin1'
, pulando obase64
que não é necessário.Apenas dicionários, listas e tipos de objetos primitivos (int, string, bool) estão disponíveis no JSON.
fonte
Você não precisa criar uma classe de codificador personalizada para fornecer o
default
método - ele pode ser passado como um argumento de palavra-chave:resulta em
[1, 2, 3]
todas as versões suportadas do Python.fonte
Se você precisar codificar apenas conjuntos, não objetos gerais do Python, e quiser mantê-lo facilmente legível por humanos, uma versão simplificada da resposta de Raymond Hettinger poderá ser usada:
fonte
Se você precisar de um despejo rápido e não quiser implementar um codificador personalizado. Você pode usar o seguinte:
json_string = json.dumps(data, iterable_as_array=True)
Isso converterá todos os conjuntos (e outras iteráveis) em matrizes. Apenas tome cuidado para que esses campos permaneçam matrizes quando você analisa o json de volta. Se você deseja preservar os tipos, precisa escrever um codificador personalizado.
fonte
Uma falha da solução aceita é que sua saída é muito específica para python. Ou seja, sua saída json bruta não pode ser observada por um ser humano ou carregada por outro idioma (por exemplo, javascript). exemplo:
Você receberá:
Posso propor uma solução que faça o downgrade do conjunto para um ditado que contém uma lista na saída e volte para um conjunto quando carregado no python usando o mesmo codificador, preservando, portanto, a observabilidade e o agnosticismo da linguagem:
O que você recebe:
Observe que a serialização de um dicionário que possui um elemento com uma chave
"__set__"
interromperá esse mecanismo. Então__set__
agora se tornou umadict
chave reservada . Obviamente, sinta-se à vontade para usar outra tecla mais ofuscada.fonte