ORJSON is an alternative library to Python's json support that lets you work with dataclasses, datetimes and numpy.

orjson features:

  • Serializes dataclass.
  • Serializes datetime, numpy.ndarray and UUID objects natively.
  • Serializes to bytes instead of string.
  • Serializes strings without escaping unicode to ASCII.
  • Faster to serialize and deserialize.
  • It has no load() and dump() to read from or write to a file.

Installation

To install, pip will be used:

pip install orjson

Implementation

  1. First, the module is imported:

import orjson
from datetime import datetime
import uuid

In addition, datetime and uuid are imported.

  1. Define the data.

dato = {
    "emoji_lagrimas": "😂",
    "emoji_reloj": "⏰",
    "entero": 123,
    "flotante": 10.4,
    "boleano": False,
    "lista": ["element1", "element2"],
    "diccionario": {"key1": "value1", "key2": "value2"},
    "ruso": "Привет",
    "chino": "您好",
    "japones": "こんにちは",
    "datetime": datetime.now(),
    "uuid": uuid.uuid1()
}

print(data)

{'emoji_tears': '😂', 'emoji_clock': '⏰', 'integer': 123, 'float': 10.4, 'boolean': False, 'list': ['element1', 'element2'], 'dict': {'key1': 'value1', 'key2': 'value2'}, 'russian': 'Привет', 'chinese': '您好', 'japanese': 'こんにちは', 'datetime': datetime.datetime(2022, 1, 20, 19, 25, 28, 555227), 'uuid': UUID('40d49edc-7a48-11ec-865d-9078414427c1')}

We have a dictionary with several data types and strings.

When printing the data, the UUID and datetime objects are shown.

  1. Serialize and return bytes instead of a string

json_byte = orjson.dumps(data)

print(json_byte)

b'{"emoji_tears":"\xf0\x9f\x98\x82","emoji_clock":"\xe2\x8f\xb0","integer":123,"float":10.4,"boolean":false,"list":["element1","element2"],"dict":{"key1":"value1","key2":"value2"},"russian":"\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82","chinese":"\xe6\x82\xa8\xe5\xa5\xbd","japanese":"\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf","datetime":"2022-01-20T19:20:56.518677","uuid":"9f8d3b92-7a47-11ec-865d-9078414427c1"}'

As the output shows, we now have bytes instead of a string. The datetime and uuid fields are no longer objects.

  1. Use the json module

When trying to convert the data dictionary to json, it will return an error.

json.dumps(data)
  1. Deserialize json_byte

json_data = orjson.loads(json_byte)

print(json_data)

{'emoji_tears': '😂',
 'emoji_clock': '⏰',
 'integer': 123,
 'float': 10.4,
 'boolean': False,
 'list': ['element1', 'element2'],
 'dict': {'key1': 'value1', 'key2': 'value2'},
 'russian': 'Привет',
 'chinese': '您好',
 'japanese': 'こんにちは',
 'datetime': '2022-01-20T19:25:28.555227',
 'uuid': '40d49edc-7a48-11ec-865d-9078414427c1'}

When deserializing, the objects are already strings.

Additional arguments for serialization

The following parameters can be passed:

  • default: A callable that returns a compatible type.
  • option: To modify how data is serialized through an integer constant in orjson.

Default

If there is a data entry of type Decimal.


import decimal
orjson.dumps(decimal.Decimal("3.141592653"))

Trying to convert a decimal returns an error:


TypeError: Type is not JSON serializable. decimal.Decimal

This is solved by using the Default parameter.


import decimal
import math 
def default(obj):
    if isinstance(obj, decimal.Decimal):
        return str(obj)
    raise TypeError
orjson.dumps(decimal.Decimal(f"{math.pi}"), default=default)

b'"3.141592653589793"'

In this case a string is passed that contains the value of pi using the math library, and it is passed with the default argument along with a function that evaluates whether it is a decimal and converts it into a string, otherwise it raises an error.

In the end, instead of returning an error, it returns bytes with the value of pi.

Option

It lets you define the serialization method.



import orjson
import datetime
import numpy as np
data = {
"datetime": datetime.datetime.now(),
"numpy": np.array([[1, 2], [3, 4]])
}
json_byte = orjson.dumps(data, option=orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY)
print(json_byte)

print(orjson.loads(json_byte))

b'{"datetime":"2022-01-20T19:48:45.924867+00:00","numpy":[[1,2],[3,4]]}'

{'datetime': '2022-01-20T19:48:45.924867+00:00', 'numpy': [[1, 2], [3, 4]]}

In this case the following options are passed:

  • orjson.OPT_NAIVE_UTC: to make the datetime conversion easier.
  • orjson.OPT_SERIALIZE_NUMPY: to make the numpy conversion easier.

Details:

  • OPT_APPEND_NEWLINE — Appends \n .
  • OPT_INDENT_2 — Indents the output by 2 spaces .
  • OPT_NAIVE_UTC — Serializes datetime.datetime objects without tzinfo as UTC.
  • OPT_SERIALIZE_NUMPY — Serializes numpy.ndarray instances.

Dataclass

ORJSON serializes dataclass as maps, with each attribute serialized.


import dataclasses, orjson, typing
@dataclasses.dataclass
class Person:
    id: int
    name: str
    status: bool = dataclasses.field(default=True)
@dataclasses.dataclass
class Class:
    id: int
    name: str
    students: typing.List[Person]
data = Class(1, "Class A", [Person(1, "John Doe", False), Person(2, "Mary Sue")])
json_byte = orjson.dumps(data)
print(json_byte)
print(orjson.loads(json_byte))


b'{"id":1,"name":"Class A","students":[{"id":1,"name":"John Doe","status":false},{"id":2,"name":"Mary Sue","status":true}]}'
{'id': 1,
 'name': 'Class A',
 'students': [{'id': 1, 'name': 'John Doe', 'status': False},
  {'id': 2, 'name': 'Mary Sue', 'status': True}]}

In this example the Person class is defined with three arguments id (integer), name (string) and status (boolean); for status the default value True is defined.

The Class class is also defined with id (integer), name (string) and students (which is a list of persons).

Then the data variable is defined, which is an instance of Class with its data.

Then it is serialized and deserialized.

Writing/Reading files

To write to a file we use the data variable from the previous example.


with open("example.json", "wb") as f:
    f.write(orjson.dumps(data))

Opening the file shows the json:

file_json

To read the json file:


with open("example.json", "rb") as f:
    json_data = orjson.loads(f.read())

print(json_data)

{'id': '1',
 'name': 'Class A',
 'students': [{'id': 1, 'name': 'John Doe', 'status': False},
  {'id': 2, 'name': 'Mary Sue', 'status': True}]}

To conclude, the most useful thing about orjson is that it allows or eases the handling of datetime and uuid objects, and it eases the conversion of the decimal data type without raising an error in the conversion, as Python's json module does.

References:

  1. Choosing a faster JSON library for Python
  2. orjson — A fast json parser for python (broken link) (article in English on which this article is based)
  3. orjson performance
  4. github repository

Make a donation! If you liked the article you can make a donation with Bitcoin (BTC) using the digital wallet of your choice to the following address: 17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV

Or by scanning the QR code from your wallet:

17MtNybhdkA9GV3UNS6BTwPcuhjXoPrSzV