Subject: register_adapter Json with custom JSONEncoder

From: Hans Ginzel <[email-masked]>
Date: Mon, 13 Jul 2020 17:49:07 +0200

Hello,

how can I register an adapter with custom JSONEncoder, please.

On Stack Overflow, https://stackoverflow.com/a/55939024/2556118
I have found to use Json class
psycopg2.extensions.register_adapter(dict, psycopg2.extras.Json).

But I need to use custom JSONEncoder because of bson.ObectId type
# https://stackoverflow.com/a/16586277/2556118
import json
from bson import ObjectId
class JSONEncoder(json.JSONEncoder):
     def default(self, o):
         if isinstance(o, ObjectId):
             return str(o)
         return supper().JSONEncoder.default(self, o)

There is a parameter dumps in Json.__init__(self, adapted, dumps=None),
but how to set it when used with register_adapter(),
https://www.psycopg.org/docs/extensions.html#psycopg2.extensions.register_adapter?

Should I write myself the whole Json class?

Thank you in advance,
Hans

---

From: Daniele Varrazzo <[email-masked]>
Date: Mon, 13 Jul 2020 17:15:19 +0100

Hello,

On Mon, 13 Jul 2020 at 16:49, Hans Ginzel <[email-masked]> wrote:
[...]

> # https://stackoverflow.com/a/16586277/2556118

This doc page has more examples:
https://www.psycopg.org/docs/extras.html#json-adaptation

> but how to set it when used with register_adapter(),

You can use a partial function for instance, of the like of: `lambda
obj: Json(obj, dumps=mydumps)`.

> Should I write myself the whole Json class?

No, if you really want you can subclass the one we provide and only
override `_dumps()`: the one psycopg2 provides consists in pretty
much:

    class Json(object):
        def __init__(self, adapted, dumps=None):
            self.adapted = adapted
           self._dumps = dumps or json.dumps

        def dumps(self, obj):
            return self._dumps(obj)

so you can either create your instances using a wrapper in
register_adapter, or subclass the _dumps() method of the class: as you
can see it is equivalent. I guess the first method is better as you
don't need to care about the implementation of the `Json` class.

-- Daniele

---

From: Hans Ginzel <[email-masked]>
Date: Mon, 20 Jul 2020 13:18:55 +0200

>obj: Json(obj, dumps=mydumps)`.

Thank you, Daniele.
I have (succesfully) tried

from psycopg2.extras import Json
from json import JSONEncoder
from bson import ObjectId

class JSONEncoder(JSONEncoder):
     def default(self, o):
         if isinstance(o, ObjectId):
             return str(o)
         return supper().default(self, o)
jsonEncoder = JSONEncoder()
#psycopg2.extensions.register_adapter(dict, Json)
psycopg2.extensions.register_adapter(dict, lambda o: Json(o, dumps=jsonEncoder.encode))

and variants like

import json
psycopg2.extensions.register_adapter(dict, lambda o: Json(o, dumps = lambda oo: json.dumps(oo, default=str)))

or

from bson import json_util
psycopg2.extensions.register_adapter(dict, lambda o: Json(o, dumps=json_util.dumps))

I choosed the first one because of cached Encoder and cleanest way.

Best regards,
Hans