16.6. JSON Object
16.6.1. SetUp
>>> from pprint import pprint
>>> from dataclasses import dataclass
>>> import json
16.6.2. Encoder
>>> class User:
... def __init__(self, firstname, lastname, age=None):
... self.firstname = firstname
... self.lastname = lastname
... self.age = age
...
... def __repr__(self):
... clsname = self.__class__.__qualname__
... arguments = ', '.join(f'{k}={v!r}' for k,v in vars(self).items())
... return f'{clsname}({arguments})'
>>>
>>> DATA = [
... User('Alice', 'Apricot', age=30),
... User('Bob', 'Blackthorn', age=31),
... User('Carol', 'Corn', age=32),
... User('Dave', 'Durian', age=33),
... User('Eve', 'Elderberry', age=34),
... User('Mallory', 'Melon', age=15),
... ]
>>>
>>> def encoder(obj):
... cls = obj.__class__
... clsname = cls.__name__
... return {'_clsname': clsname} | vars(obj)
>>>
>>>
>>> result = json.dumps(DATA, default=encoder, indent=2)
>>> print(result)
[
{
"_clsname": "User",
"firstname": "Alice",
"lastname": "Apricot",
"age": 30
},
{
"_clsname": "User",
"firstname": "Bob",
"lastname": "Blackthorn",
"age": 31
},
{
"_clsname": "User",
"firstname": "Carol",
"lastname": "Corn",
"age": 32
},
{
"_clsname": "User",
"firstname": "Dave",
"lastname": "Durian",
"age": 33
},
{
"_clsname": "User",
"firstname": "Eve",
"lastname": "Elderberry",
"age": 34
},
{
"_clsname": "User",
"firstname": "Mallory",
"lastname": "Melon",
"age": 15
}
]
16.6.3. Decoder
>>> DATA = """
... [
... {
... "_clsname": "User",
... "firstname": "Alice",
... "lastname": "Apricot",
... "age": 30
... },
... {
... "_clsname": "User",
... "firstname": "Bob",
... "lastname": "Blackthorn",
... "age": 31
... },
... {
... "_clsname": "User",
... "firstname": "Carol",
... "lastname": "Corn",
... "age": 32
... },
... {
... "_clsname": "User",
... "firstname": "Dave",
... "lastname": "Durian",
... "age": 33
... },
... {
... "_clsname": "User",
... "firstname": "Eve",
... "lastname": "Elderberry",
... "age": 34
... },
... {
... "_clsname": "User",
... "firstname": "Mallory",
... "lastname": "Melon",
... "age": 15
... }
... ]
... """
>>> class User:
... def __init__(self, firstname, lastname, age=None):
... self.firstname = firstname
... self.lastname = lastname
... self.age = age
...
... def __repr__(self):
... clsname = self.__class__.__qualname__
... arguments = ', '.join(f'{k}={v!r}' for k,v in vars(self).items())
... return f'{clsname}({arguments})'
>>>
>>>
>>> def decoder(obj):
... clsname = obj.pop('_clsname')
... cls = globals()[clsname]
... return cls(**obj)
>>>
>>> json.loads(DATA, object_hook=decoder)
[User(firstname='Alice', lastname='Apricot', age=30),
User(firstname='Bob', lastname='Blackthorn', age=31),
User(firstname='Carol', lastname='Corn', age=32),
User(firstname='Dave', lastname='Durian', age=33),
User(firstname='Eve', lastname='Elderberry', age=34),
User(firstname='Mallory', lastname='Melon', age=15)]
16.6.4. Objects with Relations
>>> class User:
... def __init__(self, firstname, lastname, age=None, groups=None):
... self.firstname = firstname
... self.lastname = lastname
... self.age = age
... self.groups = groups if groups else []
...
... def __repr__(self):
... clsname = self.__class__.__qualname__
... arguments = ', '.join(f'{k}={v!r}' for k,v in vars(self).items())
... return f'{clsname}({arguments})'
>>>
>>> class Group:
... def __init__(self, name):
... self.name = name
...
... def __repr__(self):
... return f'{self.name}'
>>>
>>>
>>> DATA = [
... User(firstname='Alice', lastname='Apricot', age=30, groups=[
... Group('users'),
... Group('staff'),
... ]),
...
... User(firstname='Bob', lastname='Blackthorn', age=31, groups=[
... Group('users'),
... Group('staff'),
... ]),
...
... User(firstname='Carol', lastname='Corn', age=32, groups=[
... Group('users'),
... ]),
...
... User(firstname='Dave', lastname='Durian', age=33, groups=[
... Group('users'),
... ]),
...
... User(firstname='Eve', lastname='Elderberry', age=34, groups=[
... Group('users'),
... Group('staff'),
... Group('admins'),
... ]),
...
... User(firstname='Mallory', lastname='Melon', age=15, groups=[]),
... ]
Encoder:
>>> def encoder(obj):
... cls = obj.__class__
... clsname = cls.__name__
... return {'_clsname': clsname} | vars(obj)
>>>
>>>
>>> encoded = json.dumps(DATA, default=encoder, indent=2)
>>> print(encoded)
[
{
"_clsname": "User",
"firstname": "Alice",
"lastname": "Apricot",
"age": 30,
"groups": [
{
"_clsname": "Group",
"name": "users"
},
{
"_clsname": "Group",
"name": "staff"
}
]
},
{
"_clsname": "User",
"firstname": "Bob",
"lastname": "Blackthorn",
"age": 31,
"groups": [
{
"_clsname": "Group",
"name": "users"
},
{
"_clsname": "Group",
"name": "staff"
}
]
},
{
"_clsname": "User",
"firstname": "Carol",
"lastname": "Corn",
"age": 32,
"groups": [
{
"_clsname": "Group",
"name": "users"
}
]
},
{
"_clsname": "User",
"firstname": "Dave",
"lastname": "Durian",
"age": 33,
"groups": [
{
"_clsname": "Group",
"name": "users"
}
]
},
{
"_clsname": "User",
"firstname": "Eve",
"lastname": "Elderberry",
"age": 34,
"groups": [
{
"_clsname": "Group",
"name": "users"
},
{
"_clsname": "Group",
"name": "staff"
},
{
"_clsname": "Group",
"name": "admins"
}
]
},
{
"_clsname": "User",
"firstname": "Mallory",
"lastname": "Melon",
"age": 15,
"groups": []
}
]
Decoder:
>>> def decoder(obj):
... clsname = obj.pop('_clsname')
... cls = globals()[clsname]
... return cls(**obj)
>>>
>>> json.loads(encoded, object_hook=decoder)
[User(firstname='Alice', lastname='Apricot', age=30, groups=[users, staff]),
User(firstname='Bob', lastname='Blackthorn', age=31, groups=[users, staff]),
User(firstname='Carol', lastname='Corn', age=32, groups=[users]),
User(firstname='Dave', lastname='Durian', age=33, groups=[users]),
User(firstname='Eve', lastname='Elderberry', age=34, groups=[users, staff, admins]),
User(firstname='Mallory', lastname='Melon', age=15, groups=[])]
16.6.5. Assignments
# %% About
# - Name: JSON Object Dumps
# - Difficulty: medium
# - Lines: 5
# - Minutes: 5
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Serialize data from variable `DATA`
# 2. To each row add `_clsname` with an object type name
# 3. Use `json` module
# 4. Result write to variable `result`
# 5. Run doctests - all must succeed
# %% Polish
# 1. Zserializuj dane ze zmiennej `DATA`
# 2. To each row add `_clsname` with an object type name
# 3. Użyj modułu `json`
# 4. Wynik zapisz do zmiennej `result`
# 5. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> result
# ('[{"_clsname": "User", "firstname": "Alice", "lastname": "Apricot", "age": 30, "groups": []}, '
# '{"_clsname": "User", "firstname": "Bob", "lastname": "Blackthorn", "age": 31, "groups": []}, '
# '{"_clsname": "User", "firstname": "Carol", "lastname": "Corn", "age": 32, "groups": []}, '
# '{"_clsname": "User", "firstname": "Dave", "lastname": "Durian", "age": 33, "groups": []}, '
# '{"_clsname": "User", "firstname": "Eve", "lastname": "Elderberry", "age": 34, "groups": []}, '
# '{"_clsname": "User", "firstname": "Mallory", "lastname": "Melon", "age": 15, "groups": []}]')
# %% Hints
# - `dict()`
# - `dict.update()`
# - `object.__class__.__name__`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> assert type(result)
>>> from pprint import pprint
>>> pprint(result, width=100)
('[{"_clsname": "User", "firstname": "Alice", "lastname": "Apricot", "age": 30, "groups": []}, '
'{"_clsname": "User", "firstname": "Bob", "lastname": "Blackthorn", "age": 31, "groups": []}, '
'{"_clsname": "User", "firstname": "Carol", "lastname": "Corn", "age": 32, "groups": []}, '
'{"_clsname": "User", "firstname": "Dave", "lastname": "Durian", "age": 33, "groups": []}, '
'{"_clsname": "User", "firstname": "Eve", "lastname": "Elderberry", "age": 34, "groups": []}, '
'{"_clsname": "User", "firstname": "Mallory", "lastname": "Melon", "age": 15, "groups": []}]')
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
import json
# %% Types
from typing import Callable
encoder: Callable[[object], dict]
result: str
# %% Data
class User:
def __init__(self, firstname, lastname, age=None, groups=None):
self.firstname = firstname
self.lastname = lastname
self.age = age
self.groups = groups if groups else []
def __repr__(self):
clsname = self.__class__.__qualname__
arguments = ', '.join(f'{k}={v!r}' for k,v in vars(self).items())
return f'{clsname}({arguments})'
DATA = [
User('Alice', 'Apricot', age=30),
User('Bob', 'Blackthorn', age=31),
User('Carol', 'Corn', age=32),
User('Dave', 'Durian', age=33),
User('Eve', 'Elderberry', age=34),
User('Mallory', 'Melon', age=15),
]
# %% Result
result = ...
# %% About
# - Name: JSON Object Dataclass
# - Difficulty: easy
# - Lines: 5
# - Minutes: 5
# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author
# %% English
# 1. Deserialize data from variable `DATA`
# 2. Use `json` module
# 3. Result write to variable `result`
# 4. Run doctests - all must succeed
# %% Polish
# 1. Zdeserializuj dane ze zmiennej `DATA`
# 2. Użyj modułu `json`
# 3. Wynik zapisz do zmiennej `result`
# 4. Uruchom doctesty - wszystkie muszą się powieść
# %% Expected
# >>> result
# [User(firstname='Alice', lastname='Apricot', age=30, groups=[]),
# User(firstname='Bob', lastname='Blackthorn', age=31, groups=[]),
# User(firstname='Carol', lastname='Corn', age=32, groups=[]),
# User(firstname='Dave', lastname='Durian', age=33, groups=[]),
# User(firstname='Eve', lastname='Elderberry', age=34, groups=[]),
# User(firstname='Mallory', lastname='Melon', age=15, groups=[])]
# %% Hints
# - `dict.pop()`
# - `globals()`
# - `cls(**obj)`
# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'
>>> type(result)
<class 'list'>
>>> len(result) > 0
True
>>> all(type(row) is User
... for row in result)
True
>>> from pprint import pprint
>>> pprint(result)
[User(firstname='Alice', lastname='Apricot', age=30, groups=[]),
User(firstname='Bob', lastname='Blackthorn', age=31, groups=[]),
User(firstname='Carol', lastname='Corn', age=32, groups=[]),
User(firstname='Dave', lastname='Durian', age=33, groups=[]),
User(firstname='Eve', lastname='Elderberry', age=34, groups=[]),
User(firstname='Mallory', lastname='Melon', age=15, groups=[])]
"""
# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`
# %% Imports
import json
# %% Types
from typing import Callable
decoder: Callable[[dict], object]
result: list[object]
# %% Data
DATA = (
'[{"_clsname": "User", "firstname": "Alice", "lastname": "Apricot", "age": 30, "groups": []}, '
'{"_clsname": "User", "firstname": "Bob", "lastname": "Blackthorn", "age": 31, "groups": []}, '
'{"_clsname": "User", "firstname": "Carol", "lastname": "Corn", "age": 32, "groups": []}, '
'{"_clsname": "User", "firstname": "Dave", "lastname": "Durian", "age": 33, "groups": []}, '
'{"_clsname": "User", "firstname": "Eve", "lastname": "Elderberry", "age": 34, "groups": []}, '
'{"_clsname": "User", "firstname": "Mallory", "lastname": "Melon", "age": 15, "groups": []}]'
)
class User:
def __init__(self, firstname, lastname, age=None, groups=None):
self.firstname = firstname
self.lastname = lastname
self.age = age
self.groups = groups if groups else []
def __repr__(self):
clsname = self.__class__.__qualname__
arguments = ', '.join(f'{k}={v!r}' for k,v in vars(self).items())
return f'{clsname}({arguments})'
# %% Result
result = ...