Skip to content

Commit

Permalink
fix JSONContent conversion of binary columns
Browse files Browse the repository at this point in the history
  • Loading branch information
phenobarbital committed Jun 30, 2023
1 parent cb1d104 commit 7980dd4
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
48 changes: 43 additions & 5 deletions navigator/libs/json.pyx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
# cython: language_level=3, embedsignature=True, boundscheck=False, wraparound=True, initializedcheck=False
# Copyright (C) 2018-present Jesus Lara
#
"""
JSON Encoder, Decoder.
"""
import uuid
import logging
from asyncpg.pgproto import pgproto
from datetime import datetime
from dataclasses import _MISSING_TYPE, MISSING
from psycopg2 import Binary # Import Binary from psycopg2
from typing import Any, Union
from pathlib import PosixPath, PurePath, Path
from decimal import Decimal
from pathlib import PurePath, PosixPath, Path
from navigator.exceptions.exceptions cimport ValidationError
import orjson

Expand All @@ -23,14 +30,21 @@ cdef class JSONContent:
def default(self, object obj):
if isinstance(obj, Decimal):
return float(obj)
elif isinstance(obj, datetime):
return str(obj)
elif hasattr(obj, "isoformat"):
return obj.isoformat()
elif isinstance(obj, pgproto.UUID):
return str(obj)
elif isinstance(obj, uuid.UUID):
return obj
elif isinstance(obj, (PosixPath, PurePath, Path)):
return str(obj)
elif hasattr(obj, "hex"):
return obj.hex
if isinstance(obj, bytes):
return obj.hex()
else:
return obj.hex
elif hasattr(obj, 'lower'): # asyncPg Range:
up = obj.upper
if isinstance(up, int):
Expand All @@ -42,13 +56,18 @@ cdef class JSONContent:
return None
elif obj == MISSING:
return None
raise TypeError(f"{obj!r} is not JSON serializable")
elif isinstance(obj, Binary): # Handle bytea column from PostgreSQL
return str(obj) # Convert Binary object to string
logging.error(f'{obj!r} of Type {type(obj)} is not JSON serializable')
raise TypeError(
f'{obj!r} of Type {type(obj)} is not JSON serializable'
)

def encode(self, object obj, **kwargs) -> str:
# decode back to str, as orjson returns bytes
options = {
"default": self.default,
"option": orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY| orjson.OPT_UTC_Z
"option": orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_PASSTHROUGH_DATETIME # | orjson.OPT_NAIVE_UTC
}
if kwargs:
options = {**options, **kwargs}
Expand All @@ -59,11 +78,15 @@ cdef class JSONContent:
).decode('utf-8')
except orjson.JSONEncodeError as ex:
raise ValidationError(
f"Invalid JSON data: {ex}"
f"Invalid JSON: {ex}"
)

dumps = encode

@classmethod
def dump(cls, object obj, **kwargs):
return cls().encode(obj, **kwargs)

def decode(self, object obj):
try:
return orjson.loads(
Expand All @@ -76,9 +99,24 @@ cdef class JSONContent:

loads = decode

@classmethod
def load(cls, object obj, **kwargs):
return cls().decode(obj, **kwargs)


cpdef str json_encoder(object obj):
return JSONContent().dumps(obj)

cpdef object json_decoder(object obj):
return JSONContent().loads(obj)

cdef class BaseEncoder:
"""
Encoder replacement for json.dumps using orjson
"""
def __init__(self, *args, **kwargs):
# Filter/adapt JSON arguments to ORJSON ones
rjargs = ()
rjkwargs = {}
encoder = JSONContent(*rjargs, **rjkwargs)
self.encode = encoder.__call__
2 changes: 1 addition & 1 deletion navigator/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
__description__ = (
"Navigator Web Framework based on aiohttp, " "with batteries included."
)
__version__ = "2.6.25"
__version__ = "2.6.26"
__author__ = "Jesus Lara"
__author_email__ = "jesuslarag@gmail.com"
__license__ = "BSD"

0 comments on commit 7980dd4

Please sign in to comment.