from .oids import builtins
# Register default adapters
-from . import array, composite, numeric, text # noqa
+from . import array, json, composite, numeric, text # noqa
# Register associations with array oids
array.register_all_arrays()
--- /dev/null
+"""
+Adapers for JSON types.
+"""
+
+# Copyright (C) 2020 The Psycopg Team
+
+import json
+import codecs
+from typing import Any, Callable, Optional
+
+from .oids import builtins
+from ..adapt import Dumper
+from ..proto import EncodeFunc
+
+_encode_utf8 = codecs.lookup("utf8").encode
+
+JSON_OID = builtins["json"].oid
+JSONB_OID = builtins["jsonb"].oid
+
+JsonDumpsFunction = Callable[[Any], str]
+
+
+class _JsonWrapper:
+ def __init__(self, obj: Any, dumps: Optional[JsonDumpsFunction] = None):
+ self.obj = obj
+ self._dumps: JsonDumpsFunction = dumps or json.dumps
+
+ def dumps(self) -> str:
+ return self._dumps(self.obj)
+
+
+class Json(_JsonWrapper):
+ pass
+
+
+class JsonB(_JsonWrapper):
+ pass
+
+
+class _JsonDumper(Dumper):
+ _oid: int
+
+ def dump(
+ self, obj: _JsonWrapper, __encode: EncodeFunc = _encode_utf8
+ ) -> bytes:
+ return __encode(obj.dumps())[0]
+
+ @property
+ def oid(self) -> int:
+ return self._oid
+
+
+@Dumper.text(Json)
+class JsonDumper(_JsonDumper):
+ _oid = JSON_OID
+
+
+@Dumper.text(JsonB)
+class JsonBDumper(_JsonDumper):
+ _oid = JSONB_OID
--- /dev/null
+import json
+
+import pytest
+
+from psycopg3.types.json import Json, JsonB
+
+samples = [
+ "null",
+ "true",
+ '"te\'xt"',
+ '"\\u00e0\\u20ac"',
+ "123",
+ "123.45",
+ '["a", 100]',
+ '{"a": 100}',
+]
+
+
+@pytest.mark.parametrize("val", samples)
+def test_json_dump(conn, val):
+ obj = json.loads(val)
+ cur = conn.cursor()
+ cur.execute("select pg_typeof(%s) = 'json'::regtype", (Json(obj),))
+ assert cur.fetchone()[0] is True
+ cur.execute("select %s::text = %s::json::text", (Json(obj), val))
+ assert cur.fetchone()[0] is True
+
+
+@pytest.mark.parametrize("val", samples)
+def test_jsonb_dump(conn, val):
+ obj = json.loads(val)
+ cur = conn.cursor()
+ cur.execute("select %s = %s::jsonb", (JsonB(obj), val))
+ assert cur.fetchone()[0] is True