Serializing / encoding¶
ujson5 API is similar to the standard json module with some additional features to support JSON5 syntax. If you are familiar with the json module, you will find ujson5 easy to use. Like the json module, ujson5 provides two main functions for encoding python objects to JSON5 strings: dump and dumps.
When using dump or dumps functions, you can either pass in parameters or pass in a subclass of JSON5Encoder to customize the serialization process. When passing in the default parameter, you can exploit types exposed by the ujson5 module to enforce type checking. Here is an example:
import ujson5
from typing import Any
def default(obj: Any) -> ujson5.Serializable:
if isinstance(obj, complex):
return {"__complex__": True, "real": obj.real, "imag": obj.imag}
elif isinstance(obj, set):
return list(obj)
raise TypeError(
f"Object of type '{obj.__class__.__name__}' is not JSON serializable"
)
data = {"complex": 1 + 2j}
serialized = ujson5.dumps(data, default=default)
print(serialized)
# {"complex": {"__complex__": true, "real": 1.0, "imag": 2.0}}
Comments Extraction¶
Warning
Comment extraction is currently only fully supported on Python 3.12+. On older versions, the function will still work but will not extract all comments from the parent TypedDicts.
Note
Comments will only be extracted and added when indent argument to dumps, dump or JSON5Encoder is set to a non-None value. Because if indent is None, the output will be a single line string and comments will not be added.
JSON5 supports adding comments to the data using the // and /* */ syntax. These comments are ignored during the parsing process. Here is an example:
{
// Single line comment
"key": "value",
/* Multi-line
comment */
"key2": "value2"
}
ujson5 makes it easy to encode python objects with comments to json5 strings with comments.
Here is an example to show how it works.
from typing import TypedDict
import ujson5
class Courses(TypedDict, total=False):
# you can also add comments in the TypedDict
CS101: int
# Multi-line comments are also supported
# In this case, the comments in JSON5 will also be multi-line
ART101: int
# You can also add comments to the TypedDict attributes
HIS101: int # a comment can also be in-line
# if a dictionary does not contain all the keys, only the keys that are
# present will be commented
LIT101: int
my_courses = Courses(CS101=1, ART101=2, HIS101=3)
serialized = ujson5.dumps(my_courses, typed_dict_cls=Courses, indent=4)
print(serialized)
# {
# // you can also add comments in the TypedDict
# "CS101": 1, // Multi-line comments are also supported
# // In this case, the comments in JSON5 will also be multi-line
# "ART101": 2, // You can also add comments to the TypedDict attributes
# "HIS101": 3, // a comment can also be in-line
# }
As mentioned in the official JSON5 website:
JSON5 is an extension to the popular JSON file format that aims to be easier to write and maintain by hand (e.g. for config files).
Comments extraction is a very handy feature if you are going to use TypeDict to validate your config files. Just make sure to comment your TypeDict attributes and ujson5 will take care of propagating your comments to the JSON5 output so that you can easily maintain your config files.
Here is a more complex example involving composite and inherited TypedDicts:
from typing import TypedDict
import ujson5
class Courses(TypedDict, total=False):
# you can also add comments in the TypedDict
CS101: int
# Multi-line comments are also supported
# In this case, the comments in JSON5 will also be multi-line
# The entries of dictionaries that implement this TypedDict will be commented
ART101: int
HIS101: int # a comment can also be in-line
# if a dictionary does not contain all the keys, only the keys that are
# present will be commented
LIT101: int
class Creature(TypedDict):
height: int # height of the creature
# weight of the creature
# weight cannot be too high!
weight: int
class Human(Creature): # (1)
# age of the human
age: int # human can be very old!
# name of the human
name: str
# human can be very intelligent
courses: Courses # hard-working human (2)
hobbies: list[str] # hobbies takes a lot of time...
- The
HumanTypedDict inherits from theCreatureTypedDict. This means that theHumanTypedDict will have all the attributes of theCreatureTypedDict in addition to its own attributes. Dictionary entries that implement theHumanTypedDict will have comments for theCreatureTypedDict attributes as well as theHumanTypedDict attributes. - The
coursesattribute of theHumanTypedDict is a dictionary that implements theCoursesTypedDict. This means that the dictionary entries that implement theHumanTypedDict will have comments for theCoursesTypedDict attributes as well as theHumanTypedDict attributes.
Let's define some dictionaries that implement the Human TypedDict:
bob: Human = {
"height": 180,
"weight": 70,
"age": 30,
"name": "Austin",
"courses": {
"CS101": 90,
"ART101": 80,
"HIS101": 70,
},
"hobbies": ["reading", "swimming", "coding"],
}
bob_str: str = ujson5.dumps(bob, typed_dict_cls=Human, indent=4)
print(bob_str)
# {
# "height": 180, // height of the creature
# // weight of the creature
# // weight cannot be too high!
# "weight": 70,
# // (1)
# // age of the human
# "age": 30, // human can be very old!
# // name of the human
# "name": "Austin", // human can be very intelligent
# "courses": {
# // you can also add comments in the TypedDict
# "CS101": 90, // Multi-line comments are also supported
# // In this case, the comments in JSON5 will also be multi-line
# // The entries of dictionaries that implement this TypedDict will be commented
# "ART101": 80,
# "HIS101": 70, // a comment can also be in-line
# }, // hard-working human (2)
# "hobbies": [
# "reading",
# "swimming",
# "coding",
# ], // hobbies takes a lot of time...
# }
jack: Human = {
"height": 170,
"weight": 80,
"age": 25,
"name": "Jack",
"courses": {
"CS101": 23,
"ART101": 67,
"LIT101": 12,
},
"hobbies": ["tennis", "writing", "coding"],
}
jack_str: str = ujson5.dumps(jack, typed_dict_cls=Human, indent=4)
print(jack_str)
# {
# "height": 170, // height of the creature
# // weight of the creature
# // weight cannot be too high!
# "weight": 80,
# // (1)
# // age of the human
# "age": 25, // human can be very old!
# // name of the human
# "name": "Jack", // human can be very intelligent
# "courses": {
# // you can also add comments in the TypedDict
# "CS101": 23, // Multi-line comments are also supported
# // In this case, the comments in JSON5 will also be multi-line
# // The entries of dictionaries that implement this TypedDict will be commented
# "ART101": 67,
# // if a dictionary does not contain all the keys, only the keys that are
# // present will be commented
# "LIT101": 12,
# }, // hard-working human (2)
# "hobbies": [
# "tennis",
# "writing",
# "coding",
# ], // hobbies takes a lot of time...
# }
View
Checkout the API Reference for more details on decoding.