3.2. Serialization JSON¶
3.2.1. JSON Syntax¶
JavaScript Object Notation
JSON format is similar to
dict
notation in PythonDifferences:
Coma
,
is not allowed after the last element in list or objectFields are enclosed only by double quote
"
charactertrue
andfalse
is always lower-casedInstead of
None
there isnull
camelCase
is convention, althoughsnake_case
is also valid
Example JSON file:
[{"sepalLength": 5.1, "sepalWidth": 3.5, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"},
{"sepalLength": 4.9, "sepalWidth": 3.0, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"},
{"sepalLength": false, "sepalWidth": true, "petalLength": null, "petalWidth": 0.2, "species": null}]
JSON or Python list[dict]
?:
{'mission': 'Ares 3',
'launch_date': datetime(2035, 6, 29, tzinfo=timezone.utc),
'destination': 'Mars',
'destination_landing': datetime(2035, 11, 7, tzinfo=timezone.utc),
'destination_location': 'Acidalia Planitia',
'crew': [{'astronaut': 'Melissa Lewis', 'date_of_birth': date(1995, 7, 15)},
{'astronaut': 'Rick Martinez', 'date_of_birth': date(1996, 1, 21)},
{'astronaut': 'Alex Vogel', 'date_of_birth': date(1994, 11, 15)},
{'astronaut': 'Chris Beck', 'date_of_birth': date(1999, 8, 2)},
{'astronaut': 'Beth Johansen', 'date_of_birth': date(2006, 5, 9)},
{'astronaut': 'Mark Watney', 'date_of_birth': date(1994, 10, 12)}]}
JSON or Python list[dict]
?:
{"mission": "Ares 3",
"launch_date": "2035-06-29T00:00:00+00:00",
"destination": "Mars",
"destination_landing": "2035-11-07T00:00:00+00:00",
"destination_location": "Acidalia Planitia",
"crew": [{"astronaut": "Melissa Lewis", "date_of_birth": "1995-07-15"},
{"astronaut": "Rick Martinez", "date_of_birth": "1996-01-21"},
{"astronaut": "Alex Vogel", "date_of_birth": "1994-11-15"},
{"astronaut": "Chris Beck", "date_of_birth": "1999-08-02"},
{"astronaut": "Beth Johansen", "date_of_birth": "2006-05-09"},
{"astronaut": "Mark Watney", "date_of_birth": "1994-10-12"}]}
JSON or Python list[dict]
?:
[{"firstname": "Jan", "lastname": "Twardowski", "addresses": [
{"street": "Kamienica Pod św. Janem Kapistranem", "city": "Kraków", "post_code": "31-008", "region": "Małopolskie", "country": "Poland"}]},
{"firstname": "José", "lastname": "Jiménez", "addresses": [
{"street": "2101 E NASA Pkwy", "city": "Houston", "post_code": 77058, "region": "Texas", "country": "USA"},
{"street": "", "city": "Kennedy Space Center", "post_code": 32899, "region": "Florida", "country": "USA"}]},
{"firstname": "Mark", "lastname": "Watney", "addresses": [
{"street": "4800 Oak Grove Dr", "city": "Pasadena", "post_code": 91109, "region": "California", "country": "USA"},
{"street": "2825 E Ave P", "city": "Palmdale", "post_code": 93550, "region": "California", "country": "USA"}]},
{"firstname": "Иван", "lastname": "Иванович", "addresses": [
{"street": "", "city": "Космодро́м Байкону́р", "post_code": "", "region": "Кызылординская область", "country": "Қазақстан"},
{"street": "", "city": "Звёздный городо́к", "post_code": 141160, "region": "Московская область", "country": "Россия"}]},
{"firstname": "Melissa", "lastname": "Lewis", "addresses": []},
{"firstname": "Alex", "lastname": "Vogel", "addresses": [
{"street": "Linder Hoehe", "city": "Köln", "post_code": 51147, "region": "North Rhine-Westphalia", "country": "Germany"}]}]
3.2.2. Mapping to JSON¶
json.dumps(DATA: dict) -> str
json.loads(DATA: str) -> dict
Serializing mapping to JSON:
import json
DATA = {'firstname': 'Mark',
'lastname': 'Watney'}
result = json.dumps(DATA)
type(result)
# <class 'str'>
print(result)
# {"firstname": "Mark", "lastname": "Watney"}
Deserializing mapping from JSON:
import json
DATA = '{"firstname": "Mark", "lastname": "Watney"}'
result = json.loads(DATA)
type(result)
# <class 'dict'>
print(result)
# {'firstname': 'Mark', 'lastname': 'Watney'}
3.2.3. Sequence to JSON¶
json.dumps(data: Sequence[dict]) -> str
json.loads(data: str) -> list[dict]
Serializing sequence to JSON:
import json
DATA = [{'firstname': 'Melissa', 'lastname': 'Lewis'},
{'firstname': 'Rick', 'lastname': 'Martinez'},
{'firstname': 'Mark', 'lastname': 'Watney'}]
result = json.dumps(DATA)
type(result)
# <class 'str'>
print(result)
# [{"firstname": "Melissa", "lastname": "Lewis"},
# {"firstname": "Rick", "lastname": "Martinez"},
# {"firstname": "Mark", "lastname": "Watney"}]
import json
DATA = '[{"firstname": "Melissa", "lastname": "Lewis"}, {"firstname": "Rick", "lastname": "Martinez"}, {"firstname": "Mark", "lastname": "Watney"}]'
result = json.loads(DATA)
type(result)
# <class 'list'>
print(result)
# [{'firstname': 'Melissa', 'lastname': 'Lewis'},
# {'firstname': 'Rick', 'lastname': 'Martinez'},
# {'firstname': 'Mark', 'lastname': 'Watney'}]
import json
DATA = [('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor')]
json.dumps(DATA)
# [["Sepal length", "Sepal width", "Petal length", "Petal width", "Species"],
# [5.8, 2.7, 5.1, 1.9, "virginica"],
# [5.1, 3.5, 1.4, 0.2, "setosa"],
# [5.7, 2.8, 4.1, 1.3, "versicolor"]]
import json
from pprint import pprint
DATA = '[["Sepal length", "Sepal width", "Petal length", "Petal width", "Species"], [5.8, 2.7, 5.1, 1.9, "virginica"], [5.1, 3.5, 1.4, 0.2, "setosa"], [5.7, 2.8, 4.1, 1.3, "versicolor"]]'
json.loads(DATA)
# [['Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'],
# [5.8, 2.7, 5.1, 1.9, 'virginica'],
# [5.1, 3.5, 1.4, 0.2, 'setosa'],
# [5.7, 2.8, 4.1, 1.3, 'versicolor']]
3.2.4. Write JSON File¶
json.dump(data: dict, file: TextIOWrapper) -> None
file extension
.json
Serialize to JSON:
import json
FILE = r'_temporary.json'
DATA = {'firstname': 'Mark',
'lastname': 'Watney'}
with open(FILE, mode='w') as file:
json.dump(DATA, file)
print(open(FILE).read())
# {"firstname": "Mark", "lastname": "Watney"}
3.2.5. Read JSON File¶
json.load(file: TextIOWrapper) -> dict
file extension
.json
Serialize to JSON:
import json
FILE = r'_temporary.json'
DATA = '{"firstname": "Mark", "lastname": "Watney"}'
open(FILE, mode='w').write(DATA)
with open(FILE) as file:
result = json.load(file)
type(result)
# <class 'dict'>
print(result)
# {'firstname': 'Mark', 'lastname': 'Watney'}
3.2.6. Datetime to JSON¶
problem with
date
,datetime
,time
Exception during encoding datetime:
from datetime import date
import json
DATA = {'firstname': 'Mark',
'lastname': 'Watney',
'date_of_birth': date(1994, 10, 12)}
result = json.dumps(DATA)
# Traceback (most recent call last):
# TypeError: Object of type date is not JSON serializable
from datetime import date
import json
DATA = {'firstname': 'Mark',
'lastname': 'Watney',
'date_of_birth': date(1994, 10, 12)}
from datetime import date
import json
DATA = {'firstname': 'Mark',
'lastname': 'Watney',
'date_of_birth': date(1994, 10, 12)}
json.JSONEncoder.default = lambda self, date: date.isoformat()
result = json.dumps(DATA)
type(result)
# <class 'str'>
print(result)
# {"firstname": "Mark", "lastname": "Watney", "date_of_birth": "1994-10-12"}
Encoder will be used, when standard procedure fails:
from datetime import date
import json
DATA = {'firstname': 'Mark',
'lastname': 'Watney',
'date_of_birth': date(1994, 10, 12),
'first_mission': datetime(1969, 7, 21, 2, 56, 15)}
class MyEncoder(json.JSONEncoder):
def default(self, value):
if isinstance(value, datetime):
return value.strftime('%Y-%m-%dT%H:%M:%S.%f+00:00')
if isinstance(value, date):
return value.strftime('%Y-%m-%d')
result = json.dumps(DATA, cls=MyEncoder)
type(result)
# <class 'str'>
print(result)
# {"firstname": "Mark",
# "lastname": "Watney",
# "date_of_birth": "1994-10-12",
# "first_mission": "1969-07-21T02:56:15.000000+00:00"}
3.2.7. JSON to Datetime¶
Simple loading returns str
not datetime
or date
:
import json
DATA = '{"firstname": "Mark", "lastname": "Watney", "date_of_birth": "1994-10-12"}'
result = json.loads(DATA)
print(result)
# {'firstname': 'Mark',
# 'lastname': 'Watney',
# 'date_of_birth': '1994-10-12'}
Simple loading returns str
not datetime
or date
:
import json
from datetime import date
DATA = '{"firstname": "Mark", "lastname": "Watney", "date_of_birth": "1994-10-12"}'
def mydecoder(data: dict) -> dict:
for field, value in data.items():
if field == 'date_of_birth':
data[field] = date.fromisoformat(value)
return data
result = json.loads(DATA, object_hook=mydecoder)
type(result)
# <class 'dict'>
print(result)
# {'firstname': 'Mark', 'lastname': 'Watney', 'date_of_birth': datetime.date(1994, 10, 12)}
Decoding datetime
and date
:
from datetime import datetime, timezone
import json
DATA = '{"name": "Jan Twardowski", "date": "1961-04-12", "datetime": "1969-07-21T02:56:15.000Z"}'
class MyDecoder(json.JSONDecoder):
def __init__(self):
super().__init__(object_hook=self.default)
def default(self, result: dict) -> dict:
for field, value in result.items():
if field in ['date', 'date_of_birth']:
value = datetime.strptime(value, '%Y-%m-%d').date()
if field in ['datetime']:
value = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc)
result[field] = value
return result
result = json.loads(DATA, cls=MyDecoder)
pprint(result)
# {'name': 'Jan Twardowski',
# 'date': datetime.date(1961, 4, 12),
# 'datetime': datetime.datetime(1969, 7, 21, 2, 56, 15, tzinfo=datetime.timezone.utc)}
from datetime import datetime, date, timezone
import json
FILE = '_temporary.json'
DATA = {"mission": "Ares 3",
"launch_date": datetime(2035, 6, 29, tzinfo=timezone.utc),
"destination": 'Mars',
"destination_landing": datetime(2035, 11, 7, tzinfo=timezone.utc),
"destination_location": "Acidalia Planitia",
"crew": [{"astronaut": 'Melissa Lewis',
"date_of_birth": date(1995, 7, 15)},
{"astronaut": 'Rick Martinez',
"date_of_birth": date(1996, 1, 21)},
{"astronaut": 'Alex Vogel',
"date_of_birth": date(1994, 11, 15)},
{"astronaut": 'Chris Beck',
"date_of_birth": date(1999, 8, 2)},
{"astronaut": 'Beth Johansen',
"date_of_birth": date(2006, 5, 9)},
{"astronaut": 'Mark Watney',
"date_of_birth": date(1994, 10, 12)}]}
class MyEncoder(json.JSONEncoder):
def default(self, value: datetime) -> str:
return value.isoformat()
class MyDecoder(json.JSONDecoder):
date_of_birth: date
launch_date: datetime
destination_landing: datetime
def __init__(self) -> None:
super().__init__(object_hook=lambda data: {
field: getattr(self, method)(value)
for field, value in data.items()
if (method := self.__annotations__.get(field, str).__name__)})
def datetime(self, value: str) -> date:
return datetime.fromisoformat(value)
def date(self, value: str) -> date:
return datetime.fromisoformat(value).date()
def str(self, value: str) -> str:
return value
result = json.dumps(DATA, cls=MyEncoder)
type(result)
# <class 'str'>
print(result)
# {"mission": "Ares 3",
# "launch_date": "2035-06-29T00:00:00+00:00",
# "destination": "Mars",
# "destination_landing": "2035-11-07T00:00:00+00:00",
# "destination_location": "Acidalia Planitia",
# "crew": [{"astronaut": "Melissa Lewis", "date_of_birth": "1995-07-15"},
# {"astronaut": "Rick Martinez", "date_of_birth": "1996-01-21"},
# {"astronaut": "Alex Vogel", "date_of_birth": "1994-11-15"},
# {"astronaut": "Chris Beck", "date_of_birth": "1999-08-02"},
# {"astronaut": "Beth Johansen", "date_of_birth": "2006-05-09"},
# {"astronaut": "Mark Watney", "date_of_birth": "1994-10-12"}]}
result = json.loads(result, cls=MyDecoder)
type(result)
# <class 'dict'>
print(result)
# {'mission': 'Ares 3',
# 'launch_date': datetime.datetime(2035, 6, 29, 0, 0, tzinfo=datetime.timezone.utc),
# 'destination': 'Mars',
# 'destination_landing': datetime.datetime(2035, 11, 7, 0, 0, tzinfo=datetime.timezone.utc),
# 'destination_location': 'Acidalia Planitia',
# 'crew': [{'astronaut': 'Melissa Lewis', 'date_of_birth': datetime.date(1995, 7, 15)},
# {'astronaut': 'Rick Martinez', 'date_of_birth': datetime.date(1996, 1, 21)},
# {'astronaut': 'Alex Vogel', 'date_of_birth': datetime.date(1994, 11, 15)},
# {'astronaut': 'Chris Beck', 'date_of_birth': datetime.date(1999, 8, 2)},
# {'astronaut': 'Beth Johansen', 'date_of_birth': datetime.date(2006, 5, 9)},
# {'astronaut': 'Mark Watney', 'date_of_birth': datetime.date(1994, 10, 12)}]}
3.2.8. Python Object to JSON¶
Encoding nested objects with relations to JSON:
import json
from dataclasses import dataclass
@dataclass
class Mission:
year: int
name: str
@dataclass
class Astronaut:
name: str
missions: list[Mission]
CREW = [
Astronaut('Melissa Lewis', []),
Astronaut('Mark Watney', missions=[
Mission(2035, 'Ares 3')]),
Astronaut('Jan Twardowski', missions=[
Mission(1969, 'Apollo 18'),
Mission(2024, 'Artemis 3')]),
]
class MyEncoder(json.JSONEncoder):
def default(self, obj):
result = obj.__dict__
result['__class_name__'] = obj.__class__.__name__
return result
result = json.dumps(CREW, cls=MyEncoder, sort_keys=True, indent=2)
print(type(result))
# <class 'str'>
print(result)
# [
# {
# "__class_name__": "Astronaut",
# "missions": [],
# "name": "Melissa Lewis"
# },
# {
# "__class_name__": "Astronaut",
# "missions": [
# {
# "__class_name__": "Mission",
# "name": "Ares 3",
# "year": 2035
# }
# ],
# "name": "Mark Watney"
# },
# {
# "__class_name__": "Astronaut",
# "missions": [
# {
# "__class_name__": "Mission",
# "name": "Apollo 18",
# "year": 1969
# },
# {
# "__class_name__": "Mission",
# "name": "Artemis 3",
# "year": 2024
# }
# ],
# "name": "Jan Twardowski"
# }
# ]
3.2.9. JSON to Python Object¶
Encoding nested objects with relations to JSON:
from dataclasses import dataclass
import json
DATA = """[
{"__class_name__": "Astronaut", "name": "Melissa Lewis", "missions": []},
{"__class_name__": "Astronaut", "name": "Mark Watney", "missions": [{"__class_name__": "Mission", "name": "Ares 3", "year": 2035}]},
{"__class_name__": "Astronaut", "name": "Jan Twardowski", "missions": [
{"__class_name__": "Mission", "name": "Apollo 18", "year": 1969},
{"__class_name__": "Mission", "name": "Artemis 3", "year": 2024}]}]"""
@dataclass
class Mission:
year: int
name: str
@dataclass
class Astronaut:
name: str
missions: list[Mission]
class MyDecoder(json.JSONDecoder):
def __init__(self):
super().__init__(object_hook=self.default)
def default(self, obj):
class_name = obj.pop('__class_name__')
cls = globals()[class_name]
return cls(**obj)
result = json.loads(DATA, cls=MyDecoder)
print(type(result))
# <class 'list'>
print(result)
# [Astronaut(name='Melissa Lewis', missions=[]),
# Astronaut(name='Mark Watney', missions=[
# Mission(year=2035, name='Ares 3')]),
# Astronaut(name='Jan Twardowski', missions=[
# Mission(year=1969, name='Apollo 18'),
# Mission(year=2024, name='Artemis 3')])]
3.2.10. Pretty Printing JSON¶
JSON can be minified to save space for network transmission
It is not very readable
Minified JSON file:
$ DATA='https://raw.githubusercontent.com/AstroMatt/book-python/master/_data/json/iris.json'
$ curl $DATA
[{"sepalLength":5.1,"sepalWidth":3.5,"petalLength":1.4,"petalWidth":0.2,"species":"setosa"},{"sepalLength":4.9,"sepalWidth":3,"petalLength":1.4,"petalWidth":0.2,"species":"setosa"},{"sepalLength":4.7,"sepalWidth":3.2,"petalLength":1.3,"petalWidth":0.2,"species":"setosa"},{"sepalLength":4.6,"sepalWidth":3.1,"petalLength":1.5,"petalWidth":0.2,"species":"setosa"},{"sepalLength":5,"sepalWidth":3.6,"petalLength":1.4,"petalWidth":0.2,"species":"setosa"},{"sepalLength":5.4,"sepalWidth":3.9,"petalLength":1.7,"petalWidth":0.4,"species":"setosa"},{"sepalLength":4.6,"sepalWidth":3.4,"petalLength":1.4,"petalWidth":0.3,"species":"setosa"},{"sepalLength":5,"sepalWidth":3.4,"petalLength":1.5,"petalWidth":0.2,"species":"setosa"},{"sepalLength":4.4,"sepalWidth":2.9,"petalLength":1.4,"petalWidth":0.2,"species":"setosa"},{"sepalLength":4.9,"sepalWidth":3.1,"petalLength":1.5,"petalWidth":0.1,"species":"setosa"},{"sepalLength":7,"sepalWidth":3.2,"petalLength":4.7,"petalWidth":1.4,"species":"versicolor"},{"sepalLength":6.4,"sepalWidth":3.2,"petalLength":4.5,"petalWidth":1.5,"species":"versicolor"},{"sepalLength":6.9,"sepalWidth":3.1,"petalLength":4.9,"petalWidth":1.5,"species":"versicolor"},{"sepalLength":5.5,"sepalWidth":2.3,"petalLength":4,"petalWidth":1.3,"species":"versicolor"},{"sepalLength":6.5,"sepalWidth":2.8,"petalLength":4.6,"petalWidth":1.5,"species":"versicolor"},{"sepalLength":5.7,"sepalWidth":2.8,"petalLength":4.5,"petalWidth":1.3,"species":"versicolor"},{"sepalLength":6.3,"sepalWidth":3.3,"petalLength":4.7,"petalWidth":1.6,"species":"versicolor"},{"sepalLength":4.9,"sepalWidth":2.4,"petalLength":3.3,"petalWidth":1,"species":"versicolor"},{"sepalLength":6.6,"sepalWidth":2.9,"petalLength":4.6,"petalWidth":1.3,"species":"versicolor"},{"sepalLength":5.2,"sepalWidth":2.7,"petalLength":3.9,"petalWidth":1.4,"species":"versicolor"},{"sepalLength":6.3,"sepalWidth":3.3,"petalLength":6,"petalWidth":2.5,"species":"virginica"},{"sepalLength":5.8,"sepalWidth":2.7,"petalLength":5.1,"petalWidth":1.9,"species":"virginica"},{"sepalLength":7.1,"sepalWidth":3,"petalLength":5.9,"petalWidth":2.1,"species":"virginica"},{"sepalLength":6.3,"sepalWidth":2.9,"petalLength":5.6,"petalWidth":1.8,"species":"virginica"},{"sepalLength":6.5,"sepalWidth":3,"petalLength":5.8,"petalWidth":2.2,"species":"virginica"},{"sepalLength":7.6,"sepalWidth":3,"petalLength":6.6,"petalWidth":2.1,"species":"virginica"},{"sepalLength":4.9,"sepalWidth":2.5,"petalLength":4.5,"petalWidth":1.7,"species":"virginica"},{"sepalLength":7.3,"sepalWidth":2.9,"petalLength":6.3,"petalWidth":1.8,"species":"virginica"},{"sepalLength":6.7,"sepalWidth":2.5,"petalLength":5.8,"petalWidth":1.8,"species":"virginica"},{"sepalLength":7.2,"sepalWidth":3.6,"petalLength":6.1,"petalWidth":2.5,"species":"virginica"}]
Pretty Printing JSON:
$ DATA='https://raw.githubusercontent.com/AstroMatt/book-python/master/_data/json/iris.json'
$ curl $DATA |python -m json.tool
[
{
"petalLength": 1.4,
"petalWidth": 0.2,
"sepalLength": 5.1,
"sepalWidth": 3.5,
"species": "setosa"
},
{
"petalLength": 1.4,
"petalWidth": 0.2,
"sepalLength": 4.9,
"sepalWidth": 3,
"species": "setosa"
},
...
json.tool
checks JSON syntax validity:
$ echo '{"sepalLength":5.1,"sepalWidth":3.5,}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 37 (char 36)
3.2.11. Assignments¶
"""
* Assignment: Serialization JSON Dump
* Complexity: easy
* Lines of code: 4 lines
* Time: 8 min
English:
1. Use data from "Given" section (see below)
2. Extract from input a header and data
3. Create `result: list[dict]`
a. key - name from the header
b. value - measurement or species
4. Write structure to file `iris_serialize.json` in JSON format
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Z danych wydziel nagłówek i pomiary
3. Wygeneruj `result: list[dict]`
a. klucz - nazwa z nagłówka
b. wartość - wyniki pomiarów lub gatunek
4. Zapisz strukturę do pliku `iris_serialize.json` w formacie JSON
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> type(result)
<class 'list'>
>>> len(result) > 0
True
>>> all(type(row) is dict
... for row in result)
True
>>> from os import remove
>>> result = open(FILE).read()
>>> remove(FILE)
>>> print(result) # doctest: +NORMALIZE_WHITESPACE
[{"Sepal length": 5.8, "Sepal width": 2.7, "Petal length": 5.1, "Petal width": 1.9, "Species": "virginica"},
{"Sepal length": 5.1, "Sepal width": 3.5, "Petal length": 1.4, "Petal width": 0.2, "Species": "setosa"},
{"Sepal length": 5.7, "Sepal width": 2.8, "Petal length": 4.1, "Petal width": 1.3, "Species": "versicolor"},
{"Sepal length": 6.3, "Sepal width": 2.9, "Petal length": 5.6, "Petal width": 1.8, "Species": "virginica"},
{"Sepal length": 6.4, "Sepal width": 3.2, "Petal length": 4.5, "Petal width": 1.5, "Species": "versicolor"},
{"Sepal length": 4.7, "Sepal width": 3.2, "Petal length": 1.3, "Petal width": 0.2, "Species": "setosa"},
{"Sepal length": 7.0, "Sepal width": 3.2, "Petal length": 4.7, "Petal width": 1.4, "Species": "versicolor"},
{"Sepal length": 7.6, "Sepal width": 3.0, "Petal length": 6.6, "Petal width": 2.1, "Species": "virginica"},
{"Sepal length": 4.9, "Sepal width": 3.0, "Petal length": 1.4, "Petal width": 0.2, "Species": "setosa"}]
"""
# Given
import json
FILE = '_temporary.json'
DATA = [('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.9, 3.0, 1.4, 0.2, 'setosa')]
"""
* Assignment: Serialization JSON Load
* Complexity: easy
* Lines of code: 4 lines
* Time: 8 min
English:
1. Use data from "Given" section (see below)
2. Read data from `FILE`
3. Convert data to `result: list[tuple]`
4. Do not add header as a first line
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Odczytaj dane z pliku `FILE`
3. Przekonwertuj dane do `result: list[tuple]`
4. Nie dodawaj nagłówka jako pierwsza linia
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> type(result)
<class 'list'>
>>> len(result) > 0
True
>>> all(type(row) is tuple
... for row in result)
True
>>> result # doctest: +NORMALIZE_WHITESPACE
[(5.8, 2.7, 5.1, 1.9, 'virginica'),
(5.1, 3.5, 1.4, 0.2, 'setosa'),
(5.7, 2.8, 4.1, 1.3, 'versicolor'),
(6.3, 2.9, 5.6, 1.8, 'virginica'),
(6.4, 3.2, 4.5, 1.5, 'versicolor'),
(4.7, 3.2, 1.3, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.9, 3.0, 1.4, 0.2, 'setosa')]
>>> from os import remove
>>> remove(FILE)
"""
import json
FILE = r'_temporary.json'
DATA = """[{"Sepal length": 5.8, "Sepal width": 2.7, "Petal length": 5.1, "Petal width": 1.9, "Species": "virginica"},
{"Sepal length": 5.1, "Sepal width": 3.5, "Petal length": 1.4, "Petal width": 0.2, "Species": "setosa"},
{"Sepal length": 5.7, "Sepal width": 2.8, "Petal length": 4.1, "Petal width": 1.3, "Species": "versicolor"},
{"Sepal length": 6.3, "Sepal width": 2.9, "Petal length": 5.6, "Petal width": 1.8, "Species": "virginica"},
{"Sepal length": 6.4, "Sepal width": 3.2, "Petal length": 4.5, "Petal width": 1.5, "Species": "versicolor"},
{"Sepal length": 4.7, "Sepal width": 3.2, "Petal length": 1.3, "Petal width": 0.2, "Species": "setosa"},
{"Sepal length": 7.0, "Sepal width": 3.2, "Petal length": 4.7, "Petal width": 1.4, "Species": "versicolor"},
{"Sepal length": 7.6, "Sepal width": 3.0, "Petal length": 6.6, "Petal width": 2.1, "Species": "virginica"},
{"Sepal length": 4.9, "Sepal width": 3.0, "Petal length": 1.4, "Petal width": 0.2, "Species": "setosa"}]"""
with open(FILE, mode='w') as file:
file.write(DATA)
result = list()
"""
* Assignment: Serialization JSON Datetime
* Complexity: easy
* Lines of code: 15 lines
* Time: 13 min
English:
1. Use data from "Given" section (see below)
2. Save data to file in JSON format
3. Read data from file
4. Recreate data structure
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Zapisz dane do pliku w formacie JSON
3. Odczytaj dane z pliku
4. Odtwórz strukturę danych
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> from inspect import isclass
>>> isclass(Encoder)
True
>>> isclass(Decoder)
True
>>> issubclass(Encoder, json.JSONEncoder)
True
>>> issubclass(Decoder, json.JSONDecoder)
True
>>> with open(FILE, mode='w') as file:
... json.dump(DATA, file, cls=Encoder)
>>> with open(FILE, mode='r') as file:
... result = json.load(file, cls=Decoder)
>>> from os import remove
>>> remove(FILE)
>>> type(result)
<class 'dict'>
>>> len(result) > 0
True
>>> all(type(key) is str
... and type(value) in (str, datetime, list)
... for key, value in result.items())
True
>>> result # doctest: +NORMALIZE_WHITESPACE
{'mission': 'Ares 3',
'launch_date': datetime.datetime(2035, 6, 29, 0, 0, tzinfo=datetime.timezone.utc),
'destination': 'Mars',
'destination_landing': datetime.datetime(2035, 11, 7, 0, 0, tzinfo=datetime.timezone.utc),
'destination_location': 'Acidalia Planitia',
'crew': [{'astronaut': 'Melissa Lewis', 'date_of_birth': datetime.date(1995, 7, 15)},
{'astronaut': 'Rick Martinez', 'date_of_birth': datetime.date(1996, 1, 21)},
{'astronaut': 'Alex Vogel', 'date_of_birth': datetime.date(1994, 11, 15)},
{'astronaut': 'Chris Beck', 'date_of_birth': datetime.date(1999, 8, 2)},
{'astronaut': 'Beth Johansen', 'date_of_birth': datetime.date(2006, 5, 9)},
{'astronaut': 'Mark Watney', 'date_of_birth': datetime.date(1994, 10, 12)}]}
"""
# Given
from datetime import datetime, date, timezone
import json
FILE = '_temporary.json'
DATA = {"mission": "Ares 3",
"launch_date": datetime(2035, 6, 29, tzinfo=timezone.utc),
"destination": 'Mars',
"destination_landing": datetime(2035, 11, 7, tzinfo=timezone.utc),
"destination_location": "Acidalia Planitia",
"crew": [{"astronaut": 'Melissa Lewis', "date_of_birth": date(1995, 7, 15)},
{"astronaut": 'Rick Martinez', "date_of_birth": date(1996, 1, 21)},
{"astronaut": 'Alex Vogel', "date_of_birth": date(1994, 11, 15)},
{"astronaut": 'Chris Beck', "date_of_birth": date(1999, 8, 2)},
{"astronaut": 'Beth Johansen', "date_of_birth": date(2006, 5, 9)},
{"astronaut": 'Mark Watney', "date_of_birth": date(1994, 10, 12)}]}
class Encoder(json.JSONEncoder):
def default(self, value: datetime) -> str:
return ...
class Decoder(json.JSONDecoder):
def __init__(self) -> None:
super().__init__(object_hook=self.default)
def default(self, data: dict) -> dict:
...
"""
* Assignment: Serialization JSON Object
* Complexity: medium
* Lines of code: 15 lines
* Time: 13 min
English:
1. Use data from "Given" section (see below)
2. Convert from JSON format to Python
3. Reading file create instances of `Setosa`, `Virginica`, `Versicolor`
classes based on value in field "species"
4. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Przekonwertuj dane z JSON do Python
3. Czytając plik twórz obiekty klas `Setosa`, `Virginica`, `Versicolor`
w zależności od wartości pola "species"
4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> type(result)
<class 'list'>
>>> len(result) > 0
True
>>> all(type(row) in (Setosa, Virginica, Versicolor)
... for row in result)
True
>>> result # doctest: +NORMALIZE_WHITESPACE
[Virginica(sepalLength=5.8, sepalWidth=2.7, petalLength=5.1, petalWidth=1.9),
Setosa(sepalLength=5.1, sepalWidth=3.5, petalLength=1.4, petalWidth=0.2),
Versicolor(sepalLength=5.7, sepalWidth=2.8, petalLength=4.1, petalWidth=1.3),
Virginica(sepalLength=6.3, sepalWidth=2.9, petalLength=5.6, petalWidth=1.8),
Versicolor(sepalLength=6.4, sepalWidth=3.2, petalLength=4.5, petalWidth=1.5),
Setosa(sepalLength=4.7, sepalWidth=3.2, petalLength=1.3, petalWidth=0.2),
Versicolor(sepalLength=7.0, sepalWidth=3.2, petalLength=4.7, petalWidth=1.4),
Virginica(sepalLength=7.6, sepalWidth=3.0, petalLength=6.6, petalWidth=2.1),
Setosa(sepalLength=4.9, sepalWidth=3.0, petalLength=1.4, petalWidth=0.2)]
"""
# Given
import json
from dataclasses import dataclass
FILE = r'_temporary.json'
DATA = """
[{"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 5.1, "petalWidth": 1.9, "species": "virginica"},
{"sepalLength": 5.1, "sepalWidth": 3.5, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"},
{"sepalLength": 5.7, "sepalWidth": 2.8, "petalLength": 4.1, "petalWidth": 1.3, "species": "versicolor"},
{"sepalLength": 6.3, "sepalWidth": 2.9, "petalLength": 5.6, "petalWidth": 1.8, "species": "virginica"},
{"sepalLength": 6.4, "sepalWidth": 3.2, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"},
{"sepalLength": 4.7, "sepalWidth": 3.2, "petalLength": 1.3, "petalWidth": 0.2, "species": "setosa"},
{"sepalLength": 7.0, "sepalWidth": 3.2, "petalLength": 4.7, "petalWidth": 1.4, "species": "versicolor"},
{"sepalLength": 7.6, "sepalWidth": 3.0, "petalLength": 6.6, "petalWidth": 2.1, "species": "virginica"},
{"sepalLength": 4.9, "sepalWidth": 3.0, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}]"""
@dataclass
class Iris:
sepalLength: float
sepalWidth: float
petalLength: float
petalWidth: float
class Setosa(Iris):
pass
class Virginica(Iris):
pass
class Versicolor(Iris):
pass
result: list
"""
* Assignment: Serialization JSON HTTP
* Complexity: hard
* Lines of code: 15 lines
* Time: 13 min
English:
1. Use data from "Given" section (see below)
1. Use `requests` library (requires installation)
2. Download data from https://api.github.com/users
3. Model data as class `User`
4. Iterate over records and create instances of this class
5. Collect all instances to one list
6. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
1. Użyj biblioteki `requests` (wymagana instalacja)
2. Pobierz dane z https://api.github.com/users
3. Zamodeluj dane za pomocą klasy `User`
4. Iterując po rekordach twórz instancje tej klasy
5. Zbierz wszystkie instancje do jednej listy
6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> type(result)
<class 'list'>
>>> len(result) > 0
True
>>> all(type(row) is User
... for row in result)
True
>>> result[0] # doctest: +NORMALIZE_WHITESPACE
User(login='mojombo',
id=1, url='https://api.github.com/users/mojombo',
node_id='MDQ6VXNlcjE=',
avatar_url='https://avatars0.githubusercontent.com/u/1?v=4',
gravatar_id='',
html_url='https://github.com/mojombo',
followers_url='https://api.github.com/users/mojombo/followers',
following_url='https://api.github.com/users/mojombo/following{/other_user}',
gists_url='https://api.github.com/users/mojombo/gists{/gist_id}',
starred_url='https://api.github.com/users/mojombo/starred{/owner}{/repo}',
subscriptions_url='https://api.github.com/users/mojombo/subscriptions',
organizations_url='https://api.github.com/users/mojombo/orgs',
repos_url='https://api.github.com/users/mojombo/repos',
events_url='https://api.github.com/users/mojombo/events{/privacy}',
received_events_url='https://api.github.com/users/mojombo/received_events',
type='User',
site_admin=False)
"""
# Given
from dataclasses import dataclass
import requests
class User:
pass
DATA = requests.get('https://api.github.com/users').json()