Como tornar uma classe Python serializável?
Uma classe simples:
class FileItem:
def __init__(self, fname):
self.fname = fname
O que devo fazer para obter a saída de:
>>> import json
>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable
Sem o erro
python
json
serialization
Sergey
fonte
fonte
import jsons
veja a resposta abaixo - ele funciona perfeitamente bemRespostas:
Você tem uma idéia sobre o resultado esperado? Por exemplo, isso fará?
Nesse caso, você pode simplesmente ligar
json.dumps(f.__dict__)
.Se você deseja uma saída mais personalizada, precisará subclassificar
JSONEncoder
e implementar sua própria serialização personalizada.Para um exemplo trivial, veja abaixo.
Então você passa essa classe para o
json.dumps()
método comocls
kwarg:Se você também quiser decodificar, precisará fornecer um costume
object_hook
para aJSONDecoder
classe. Por exemplofonte
__dict__
não funcionará em todos os casos. Se os atributos não tiverem sido definidos após a instanciação do objeto,__dict__
pode não ser totalmente preenchido. No exemplo acima, você está bem, mas se você tiver atributos de classe que também deseja codificar, eles não serão listados a__dict__
menos que tenham sido modificados na__init__
chamada da classe ou de alguma outra maneira após a instanciação do objeto.from_json()
função usada como gancho de objeto deve ter umaelse: return json_object
instrução, para que também possa lidar com objetos gerais.__dict__
também não funciona se você usar__slots__
uma nova classe de estilo.JSONEncoder
como acima para criar um protocolo personalizado, como verificar a existência do__json_serializable__
método e chamá-lo para obter uma representação serializável JSON do objeto. Isso estaria de acordo com outros padrões de Python, assim como__getitem__
,__str__
,__eq__
, e__len__
.__dict__
também não funcionará recursivamente, por exemplo, se um atributo do seu objeto for outro objeto.Aqui está uma solução simples para um recurso simples:
.toJSON()
MétodoEm vez de uma classe serializável JSON, implemente um método serializador:
Então, basta chamá-lo para serializar:
irá produzir:
fonte
o.__dict___
. Tente seu próprio exemplo:class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
a.__dict__
/b.__dict__
.datetime.datetime
instâncias. Emite o seguinte erro:'datetime.datetime' object has no attribute '__dict__'
Para classes mais complexas, você pode considerar a ferramenta jsonpickle :
(link para jsonpickle no PyPi)
fonte
jsonpickle
objeto. Além disso, isso não foi capaz de decodificar um ditado de dict contendo quadros de dados de pandas.obj = jsonpickle.decode(file.read())
efile.write(jsonpickle.encode(obj))
.A maioria das respostas envolve alterar a chamada para json.dumps () , o que nem sempre é possível ou desejável (pode acontecer dentro de um componente da estrutura, por exemplo).
Se você quiser chamar json.dumps (obj) como está, uma solução simples será herdada do dict :
Isso funciona se sua classe é apenas uma representação básica de dados, para coisas mais complicadas, você sempre pode definir chaves explicitamente.
fonte
dumps
solução não é uma boa solução. A propósito, na maioria dos casos, você provavelmente deseja terdict
herança junto com a delegação, o que significa que você terá algumdict
atributo de tipo dentro da sua classe e passará esse atributo como parâmetro, como algo como inicializaçãosuper().__init__(self.elements)
.Eu gosto da resposta de Onur, mas expandiria para incluir um
toJSON()
método opcional para objetos se serializarem:fonte
json.dumps
tratamento personalizado existente e de introdução. Obrigado!try-catch
provavelmente faria algo comoif 'toJSON' in obj.__attrs__():
... para evitar uma falha silenciosa (no caso de falha no JSON () por algum outro motivo que não esteja lá) ... uma falha que potencialmente leva à corrupção de dados.Outra opção é agrupar o dumping JSON em sua própria classe:
Ou, melhor ainda, subclassificando a classe FileItem de uma
JsonSerializable
classe:Teste:
fonte
__json__encode__
/__json_decode__
(divulgação: fiz a última).Basta adicionar um
to_json
método à sua classe como este:E adicione este código ( desta resposta ) a algum lugar no topo de tudo:
Isso fará o patch do módulo json quando for importado, e o JSONEncoder.default () verificará automaticamente um método "to_json ()" especial e o utilizará para codificar o objeto, se encontrado.
Assim como Onur disse, mas desta vez você não precisa atualizar todos os itens
json.dumps()
do seu projeto.fonte
TheObject.to_json = my_serializer
.Me deparei com esse problema outro dia e implementei uma versão mais geral de um Encoder para objetos Python que pode manipular objetos aninhados e campos herdados :
Exemplo:
Resultado:
fonte
return obj
na última linha, eu fiz issoreturn super(ObjectEncoder, self).default(obj)
. Referência AQUISe você estiver usando Python3.5 +, você pode usar
jsons
. Ele converterá seu objeto (e todos os seus atributos recursivamente) em um ditado.Ou se você quiser uma string:
Ou se sua classe implementou
jsons.JsonSerializable
:fonte
jsons
biblioteca com classes de dados . Até agora, tudo bem para mim!se usar padrão
json
, você precisa definir umadefault
funçãofonte
json.dumps(User('alice', '[email protected]'), default=lambda x: x.__dict__)
json
é limitado em termos de objetos que pode imprimir ejsonpickle
(você pode precisar de apip install jsonpickle
) é limitado em termos de não pode recuar texto. Se você deseja inspecionar o conteúdo de um objeto cuja classe você não pode alterar, ainda não consegui encontrar uma maneira mais direta do que:Nota: ainda assim eles não podem imprimir os métodos de objeto.
fonte
Esta classe pode fazer o truque, converte o objeto em json padrão.
uso:
trabalhando em
python2.7
epython3
.fonte
fonte
default(obj)
é uma função que deve retornar uma versão serializável de obj ou gerar TypeError. O padrãodefault
simplesmente gera TypeError.jaraco deu uma resposta bastante clara. Eu precisava consertar algumas coisas menores, mas isso funciona:
Código
Observe que precisamos de duas etapas para carregar. Por enquanto, a
__python__
propriedade não é usada.Quão comum é isso?
Usando o método de AlJohri , verifico a popularidade das abordagens:
Serialização (Python -> JSON):
to_json
: 266.595 em 27/06/2018toJSON
: 96.307 em 27/06/2018__json__
: 8.504 em 27/06/2018for_json
: 6.937 em 27/06/2018Desserialização (JSON -> Python):
from_json
: 226.101 em 27/06/2018fonte
Isso funcionou bem para mim:
e depois
e
fonte
Se você não se importa em instalar um pacote, pode usar o json-tricks :
Depois disso, você só precisa importar
dump(s)
de emjson_tricks
vez de json, e geralmente funcionará:o que vai dar
E é basicamente isso!
Isso funcionará muito bem em geral. Existem algumas exceções, por exemplo, se coisas especiais acontecem
__new__
ou mais magia de metaclasse está acontecendo.Obviamente, o carregamento também funciona (caso contrário, qual é o objetivo):
Isso pressupõe que
module_name.test_class.MyTestCls
possa ser importado e não tenha sido alterado de maneiras incompatíveis. Você retornará uma instância , não algum dicionário ou algo assim, e deve ser uma cópia idêntica à que você despejou.Se você deseja personalizar como algo é (des) serializado, você pode adicionar métodos especiais à sua classe, como:
que serializa apenas parte dos parâmetros dos atributos, como exemplo.
E como um bônus gratuito, você obtém (des) serialização de matrizes numpy, data e hora, mapas ordenados, bem como a capacidade de incluir comentários no json.
Isenção de responsabilidade: criei json_tricks , porque tive o mesmo problema que você.
fonte
jsonweb parece ser a melhor solução para mim. Veja http://www.jsonweb.info/en/latest/
fonte
Aqui estão meus 3 centavos ...
Isso demonstra serialização explícita de json para um objeto python do tipo árvore.
Nota: Se você realmente quisesse algum código como esse, poderia usar a classe FilePath distorcida .
fonte
Encontrei este problema ao tentar armazenar o modelo do Peewee no PostgreSQL
JSONField
.Depois de lutar um pouco, aqui está a solução geral.
A chave da minha solução é passar pelo código-fonte do Python e perceber que a documentação do código (descrita aqui ) já explica como estender o existente
json.dumps
para suportar outros tipos de dados.Suponha que você atualmente tenha um modelo que contém alguns campos que não são serializáveis para JSON e o modelo que contém o campo JSON originalmente se parece com isso:
Basta definir um costume
JSONEncoder
como este:E então basta usá-lo
JSONField
como você abaixo:A chave é o
default(self, obj)
método acima. Para cada... is not JSON serializable
reclamação que você recebe do Python, basta adicionar código para lidar com o tipo não serializável em JSON (comoEnum
oudatetime
)Por exemplo, veja como eu suporte uma classe herdada de
Enum
:Por fim, com o código implementado como acima, você pode converter qualquer modelo Peewee em um objeto seriamente JSON, como abaixo:
Embora o código acima fosse (um pouco) específico para o Peewee, mas acho que:
json.dumps
funciona, esta solução também funciona com Python (sans ORM) em geral tambémQualquer dúvida, por favor poste na seção de comentários. Obrigado!
fonte
Essa função usa recursão para iterar todas as partes do dicionário e, em seguida, chama os métodos repr () de classes que não são do tipo interno.
fonte
Esta é uma pequena biblioteca que serializa um objeto com todos os seus filhos para JSON e também o analisa:
https://github.com/Toubs/PyJSONSerialization/
fonte
Eu vim com minha própria solução. Use esse método, passe qualquer documento ( dict , list , ObjectId etc.) para serializar.
fonte
Eu escolhi usar decoradores para resolver o problema de serialização de objetos de data e hora. Aqui está o meu código:
Ao importar o módulo acima, meus outros módulos usam json de maneira normal (sem especificar a palavra-chave padrão) para serializar dados que contêm objetos de data e hora. O código do serializador de data e hora é chamado automaticamente para json.dumps e json.dump.
fonte
Eu gostei mais do método de Lost Koder. Encontrei problemas ao tentar serializar objetos mais complexos, cujos membros / métodos não são serializáveis. Aqui está minha implementação que funciona em mais objetos:
fonte
Se você é capaz de instalar um pacote, recomendo tentar o dill , que funcionou bem no meu projeto. Uma coisa boa sobre este pacote é que ele tem a mesma interface que
pickle
, portanto, se você já estiver usandopickle
em seu projeto, você pode simplesmente substituirdill
e ver se o script é executado, sem alterar nenhum código. Portanto, é uma solução muito barata para tentar!(Anti-divulgação total: não sou de forma alguma afiliado e nunca contribuí com o projeto de endro.)
Instale o pacote:
Em seguida, edite seu código para importar em
dill
vez depickle
:Execute seu script e veja se funciona. (Se isso acontecer, você pode limpar seu código para não estar mais sombreando o
pickle
nome do módulo!)Algumas especificações sobre tipos de dados que
dill
podem e não podem ser serializados, na página do projeto :fonte
Não vejo nenhuma menção aqui ao versionamento serial ou ao backcompat, por isso publicarei minha solução que venho usando há um pouco. Eu provavelmente tenho muito mais para aprender, especificamente Java e Javascript são provavelmente mais maduros do que eu aqui, mas aqui vai
https://gist.github.com/andy-d/b7878d0044a4242c0498ed6d67fd50fe
fonte
Para adicionar outra opção: Você pode usar o
attrs
pacote e oasdict
método.e converter de volta
classe se parece com isso
fonte
Além da resposta do Onur , você possivelmente deseja lidar com o tipo de data e hora como abaixo.
(para manipular: o objeto 'datetime.datetime' não possui exceção de atributo ' dict '.)
Uso:
fonte
Primeiro, precisamos tornar nosso objeto compatível com JSON, para que possamos despejá-lo usando o módulo JSON padrão. Eu fiz assim:
fonte
Com base Quinten Cabo de resposta :
As diferenças são
list
etuple
(funciona para matrizes NumPy, etc.)__dict__
).float
e,None
portanto, eles não são convertidos em string.Deixado como exercício para o leitor é lidar com
__slots__
classes iteráveis e com membros, classes que são dicionários e também com membros, etc.fonte