Please consider this toy example of a custom JSON-encoder.
import json
from typing import Any
class MyNumber:
def __init__(self, num):
self.num = num
class MultEncoder(json.JSONEncoder):
'Multiply MyNumber instances when JSON-encoding.'
def default(self, o: Any) -> Any:
if isinstance(o, MyNumber):
return o.num*2
return super().default(o)
It works like this:
>>> json.dumps({"a": MyNumber(5)}, cls=MultEncoder)
'{"a": 10}'
My question is how can I make the factor dynamic? I would like to do:
class MultEncoder(json.JSONEncoder):
'Multiply MyNumber instances when JSON-encoding.'
def __init__(self, factor, *args, **kwargs):
self.factor = factor
super().__init__(*args, **kwargs)
def default(self, o: Any) -> Any:
if isinstance(o, MyNumber):
return o.num*self.factor
return super().default(o)
But of course, json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7))
fails with
TypeError: 'MultEncoder' object is not callable
because the cls
argument is expected to be a class, not an instance.
edit1: Note that I cannot write custom my_json_dumps
/my_json_loads
functions, I can only control which encoder/decoder classes get imported in other parts of the code where json.dumps
and loads
are used.
edit2: Note that I tried to make a general, simple example. In my real code the encoder/decoder need to know credentials for connecting to a database and other dynamic configuration.
CodePudding user response:
I came up with a pretty ugly solution: a class-factory function where the returned class accesses closure-variables.
def MultEncoder(factor):
class _MultEncoder(json.JSONEncoder):
'Multiply MyNumber instances when JSON-encoding.'
def default(self, o: Any) -> Any:
if isinstance(o, MyNumber):
return o.num*factor
return super().default(o)
return _MultEncoder
Demo:
>>> json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7))
'{"a": 35}'
Is this the best we can do here?
CodePudding user response:
Make MultEncoder
class instances callable by simply adding the __call__
method.
class MyNumber:
def __init__(self, num):
self.num = num
class MultEncoder(json.JSONEncoder):
'Multiply MyNumber instances when JSON-encoding.'
def __init__(self, factor, *args, **kwargs):
self.factor = factor
super().__init__(*args, **kwargs)
def __call__(self,**kwargs):
return self
def default(self, o: Any) -> Any:
if isinstance(o, MyNumber):
return o.num*self.factor
return super().default(o)
json.dumps({"a": MyNumber(5)}, cls=MultEncoder(7))
And the output is:
'{"a": 35}'