2. JSON Serialization

Format JSON jest podobny do zapisu dict w Python, ale różni się:

  • nie może być przecinka po ostatnim elemencie list
  • zawsze są stosowane podwójne cudzysłowia
  • true i false jest pisane małymi literami
  • zamiast None jest null

2.1. JSON Serialization of simple objects

2.1.1. Serializing to JSON

Code Listing 2.14. Serializing to JSON
import json


DATA = {
    'first_name': 'José',
    'last_name': 'Jiménez'
}

output = json.dumps(DATA)
print(output)
# '{"first_name": "Jos\u00e9", "last_name": "Jim\u00e9nez"}'

2.1.2. Deserializing from JSON

Code Listing 2.15. Deserializing from JSON
import json


DATA = '{"first_name": "José", "last_name": "Jiménez"}'

output = json.loads(DATA)
print(output)
# {
#     'first_name': 'Jos\u00e9',
#     'last_name': 'Jim\u00e9nez'
# }

2.2. Serializing datetime and date

2.2.1. Encoding datetime and date

Code Listing 2.16. Exception during encoding datetime
from datetime import datetime, date
import json


DATA = {
    'email': '[email protected]',
    'date': date(1961, 4, 12),
    'datetime': datetime(1969, 7, 21, 14, 56, 15),
}

output = json.dumps(DATA)
# TypeError: Object of type date is not JSON serializable
Code Listing 2.17. Encoding datetime and date
from datetime import datetime, date
import json


DATA = {
    'email': '[email protected]',
    'date': date(1961, 4, 12),
    'datetime': datetime(1969, 7, 21, 14, 56, 15),
}


def encoder(self, value):

    if isinstance(value, datetime):
        return f'{value:%Y-%m-%dT%H:%M:%S.%fZ}'
    elif isinstance(value, date):
        return f'{value:%Y-%m-%d}'


json.JSONEncoder.default = encoder

output = json.dumps(DATA)
print(output)
# '{"email": "[email protected]", "date": "1961-04-12", "datetime": "1969-07-21T14:56:15.000Z"}'

2.2.2. Decoding datetime and date

Code Listing 2.18. Simple loading returns str not datetime or date
import json


DATA = '{"email": "[email protected]", "date": "1961-04-12", "datetime": "1969-07-21T14:56:15.000Z"}'

output = json.loads(DATA)
print(output)
# {
#     'email': '[email protected]',
#     'date': '1961-04-12',
#     'datetime': '1969-07-21T14:56:15.000Z',
# }
Code Listing 2.19. Decoding datetime and date
from datetime import datetime, timezone
import json


DATA = '{"email": "[email protected]", "date": "1961-04-12", "datetime": "1969-07-21T14:56:15.000Z"}'


def decoder(obj):
    for key, value in obj.items():

        if key == 'datetime':
            dt = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
            obj['datetime'] = dt.replace(tzinfo=timezone.utc)

        elif key == 'date':
            dt = datetime.strptime(value, '%Y-%m-%d')
            obj['date'] = dt.replace(tzinfo=timezone.utc).date()

    return obj


output = json.loads(DATA, object_hook=decoder)

print(output)
# {
#     'email': '[email protected]',
#     'date': datetime.date(1961, 4, 12),
#     'datetime': datetime.datetime(1969, 7, 21, 14, 56, 15, tzinfo=datetime.timezone.utc),
# }

2.3. Serializing objects

2.3.1. Encoding objects

Code Listing 2.20. Encoding objects to JSON
import json
from dataclasses import dataclass


@dataclass
class Address:
    street: str
    city: str
    state: str
    zipcode: str
    country: str


@dataclass
class Contact:
    first_name: str
    last_name: str
    addresses: tuple = ()


addressbook = [
    Contact(first_name='Matt', last_name='Kowalski', addresses=(
        Address(street='2101 E NASA Pkwy', city='Houston', state='Texas', zipcode='77058', country='USA'),
        Address(street='', city='Kennedy Space Center', zipcode='32899', state='Florida', country='USA'),
        Address(street='4800 Oak Grove Dr', city='Pasadena', zipcode='91109', state='California', country='USA'),
        Address(street='2825 E Ave P', city='Palmdale', state='California', zipcode='93550', country='USA'),
    )),
    Contact(first_name='José', last_name='Jiménez'),
    Contact(first_name='Иван', last_name='Иванович', addresses=()),
]


def encoder(self, obj):
    result = obj.__dict__
    result['__type__'] = obj.__class__.__name__
    return result


json.JSONEncoder.default = encoder

output = json.dumps(addressbook)
print(output)
# [{"first_name": "Matt", "last_name": "Kowalski", "addresses": [{"street": "2101 E NASA Pkwy", "city": "Houston", "state": "Texas", "zipcode": "77058", "country": "USA", "__type__": "Address"}, {"street": "", "city": "Kennedy Space Center", "state": "Florida", "zipcode": "32899", "country": "USA", "__type__": "Address"}, {"street": "4800 Oak Grove Dr", "city": "Pasadena", "state": "California", "zipcode": "91109", "country": "USA", "__type__": "Address"}, {"street": "2825 E Ave P", "city": "Palmdale", "state": "California", "zipcode": "93550", "country": "USA", "__type__": "Address"}], "__type__": "Contact"}, {"first_name": "Jos\u00e9", "last_name": "Jim\u00e9nez", "addresses": [], "__type__": "Contact"}, {"first_name": "\u0418\u0432\u0430\u043d", "last_name": "\u0418\u0432\u0430\u043d\u043e\u0432\u0438\u0447", "addresses": [], "__type__": "Contact"}]

2.3.2. Decoding objects

Code Listing 2.21. Decoding objects from JSON
import json
import sys
from dataclasses import dataclass


DATA = """[{"first_name": "Matt", "last_name": "Kowalski", "addresses": [{"street": "2101 E NASA Pkwy", "city": "Houston", "state": "Texas", "zipcode": "77058", "country": "USA", "__type__": "Address"}, {"street": "Florida", "city": "Kennedy Space Center", "state": "", "zipcode": "32899", "country": "USA", "__type__": "Address"}, {"street": "4800 Oak Grove Dr", "city": "Pasadena", "state": "California", "zipcode": "91109", "country": "USA", "__type__": "Address"}, {"street": "2825 E Ave P", "city": "Palmdale", "state": "California", "zipcode": "93550", "country": "USA", "__type__": "Address"}], "__type__": "Contact"}, {"first_name": "Jos\u00e9", "last_name": "Jim\u00e9nez", "addresses": [], "__type__": "Contact"}, {"first_name": "\u0418\u0432\u0430\u043d", "last_name": "\u0418\u0432\u0430\u043d\u043e\u0432\u0438\u0447", "addresses": [], "__type__": "Contact"}]"""


@dataclass
class Address:
    street: str
    city: str
    state: str
    zipcode: str
    country: str


@dataclass
class Contact:
    first_name: str
    last_name: str
    addresses: tuple = ()


def decoder(obj):
    type = obj.pop('__type__')
    cls = getattr(sys.modules[__name__], type)
    return cls(**obj)


output = json.loads(DATA, object_hook=decoder)
print(output)
# [
#   Contact(first_name='Matt', last_name='Kowalski', addresses=[
#       Address(street='2101 E NASA Pkwy', city='Houston', state='Texas', zipcode='77058', country='USA'),
#       Address(street='', city='Kennedy Space Center', state='Florida', zipcode='32899', country='USA'),
#       Address(street='4800 Oak Grove Dr', city='Pasadena', state='California', zipcode='91109', country='USA'),
#       Address(street='2825 E Ave P', city='Palmdale', state='California', zipcode='93550', country='USA')]),
#   Contact(first_name='José', last_name='Jiménez', addresses=[]),
#   Contact(first_name='Иван', last_name='Иванович', addresses=[])
# ]

2.4. Class based encoders and decoders

2.4.1. Class based encoder

Code Listing 2.22. Class based encoder
from datetime import datetime, date
import json


DATA = {
    'email': '[email protected]',
    'date': date(1961, 4, 12),
    'datetime': datetime(1969, 7, 21, 14, 56, 15),
}


class DatetimeEncoder(json.JSONEncoder):
    def default(self, value):

        if isinstance(value, datetime):
            return f'{value:%Y-%m-%dT%H:%M:%S.%fZ}'
        elif isinstance(value, date):
            return f'{value:%Y-%m-%d}'


output = json.dumps(DATA, cls=DatetimeEncoder)

print(output)
# '{"email": "[email protected]", "date": "1961-04-12", "datetime": "1969-07-21T14:56:15.000Z"}'

2.4.2. Class based decoder

Code Listing 2.23. Class based decoder
import datetime
import json


DATA = '{"email": "[email protected]", "date": "1961-04-12", "datetime": "1969-07-21T14:56:15.000Z"}'


class DatetimeDecoder(json.JSONDecoder):
    def __init__(self):
        super().__init__(object_hook=self.default)

    def default(self, obj):
        for key, value in obj.items():

            if key == 'datetime':
                dt = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
                obj['datetime'] = dt.replace(tzinfo=datetime.timezone.utc)

            elif key == 'date':
                dt = datetime.datetime.strptime(value, '%Y-%m-%d')
                obj['date'] = dt.date()

        return obj


output = json.loads(DATA, cls=DatetimeDecoder)

print(output)
# {
#     'email': '[email protected]',
#     'date': date(1961, 4, 12),
#     'datetime': datetime(1969, 7, 21, 14, 56, 15, tzinfo=datetime.timezone.utc),
# }

2.5. Pretty Printing JSON

$ echo '{"json": "obj"}' | python -m json.tool
{
    "json": "obj"
}
$ echo '{1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
$ curl https://api.github.com/repos/django/django/commits |python -m json.tool

Alternatywy:

2.6. Assignments

2.6.1. Iris Serialize

  1. Dane z listingu poniżej zapisz do pliku iris.json w formacie JSON

  2. Dane mają być zapisane jako list of dict

    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'),
        (4.9, 2.5, 4.5, 1.7, 'virginica'),
        (7.1, 3.0, 5.9, 2.1, 'virginica'),
        (4.6, 3.4, 1.4, 0.3, 'setosa'),
        (5.4, 3.9, 1.7, 0.4, 'setosa'),
        (5.7, 2.8, 4.5, 1.3, 'versicolor'),
        (5.0, 3.6, 1.4, 0.3, 'setosa'),
        (5.5, 2.3, 4.0, 1.3, 'versicolor'),
        (6.5, 3.0, 5.8, 2.2, 'virginica'),
        (6.5, 2.8, 4.6, 1.5, 'versicolor'),
        (6.3, 3.3, 6.0, 2.5, 'virginica'),
        (6.9, 3.1, 4.9, 1.5, 'versicolor'),
        (4.6, 3.1, 1.5, 0.2, 'setosa'),
    ]
    
About:
  • Filename: json_iris_serialize.py
  • Lines of code to write: 30 lines
  • Estimated time of completion: 20 min
The whys and wherefores:
 
  • Serializacja danych
  • Korzystanie z biblioteki JSON
  • Konwersja typów
  • Praca z plikami

2.6.2. Iris deserialize

  1. Dane z listingu poniżej skopiuj do pliku “iris.json”

    [{"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"}]
    
  2. Odczytaj dane z pliku, i wyświetl je w formacie list of tuple

  3. Pierwsza linijka ma zawierać nagłówek

  4. Kolejne linie mają mieć dane

About:
  • Filename: json_iris_deserialize.py
  • Lines of code to write: 30 lines
  • Estimated time of completion: 20 min
The whys and wherefores:
 
  • Deserializacja danych
  • Korzystanie z biblioteki JSON
  • Konwersja typów
  • Praca z plikami

2.6.3. Date serialization

  1. Skopiuj do swojego pliku strukturę danych Code Listing 2.24.
  2. Zapisz ją do pliku json
  3. Wczytaj ją z pliku json jako obiekty Pythona (ten sam efekt co na listingu)
About:
  • Filename: json_datetimes.py
  • Lines of code to write: 10 lines
  • Estimated time of completion: 15 min
The whys and wherefores:
 
  • Serializacja danych
  • Korzystanie z biblioteki JSON
  • Serializowanie zagnieżdżonych dat i dat z czasem
Code Listing 2.24. Sample Python data JSON
from datetime import datetime, date


DATA = {
    "astronaut": {
        "date": date(1961, 4, 12),
        "person": "[email protected]"
    },
    "flight": [
        {"datetime": datetime(1969, 7, 21, 14, 56, 15), "action": "landing"}
    ]
}

2.6.4. Serializing custom class to JSON

  1. Skopiuj do pliku iris.json dane z listingu Code Listing 2.25.
  2. Stwórz klasy Setosa, Virginica, Versicolor
  3. Czytając dane z pliku twórz obiekty powyższych klas w zależności od wyniku pomiaru (pole “species”)
About:
  • Filename: json_objects.py
  • Lines of code to write: 15 lines
  • Estimated time of completion: 20 min
The whys and wherefores:
 
  • Serializacja danych
  • Korzystanie z biblioteki JSON
  • Serializowanie zagnieżdżonych obiektów
Code Listing 2.25. Sample Python data JSON
[
  {"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": 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.0, "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.0, "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.0, "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.0, "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.0, "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.0, "petalWidth": 2.5, "species": "virginica"},
  {"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 5.1, "petalWidth": 1.9, "species": "virginica"},
  {"sepalLength": 7.1, "sepalWidth": 3.0, "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.0, "petalLength": 5.8, "petalWidth": 2.2, "species": "virginica"},
  {"sepalLength": 7.6, "sepalWidth": 3.0, "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"}
]

2.6.5. Deserialize data from GITHub

  1. Za pomocą biblioteki requests pobierz dane z https://api.github.com/users
  2. Iterując po rekordach twórz obiekty klasy User
About:
  • Filename: json_deserialize_github.py
  • Lines of code to write: 15 lines
  • Estimated time of completion: 20 min
The whys and wherefores:
 
  • Deserializacja danych
  • Korzystanie z biblioteki JSON
  • Deserializacja zagnieżdżonych obiektów
  • Reprezentacja klas na podstawie danych otrzymanych przez API

2.6.6. Deserialize

  1. Po API dostajesz JSONa tak jak na listingu poniżej
  2. Iterując po rekordach twórz obiekty klasy Astronaut
  3. Sparsuj user_permissions i przedstaw je za pomocą listy klas
  4. Nazwa klasy to klucz w słowniku
  5. Są zawsze cztery pola: "add", "modify", "view", "delete"
  6. Jeżeli jakieś pole jest wymienione, to ma wartość True, jeżeli nie to False
About:
  • Filename: json_deserialize_classes.py
  • Lines of code to write: 30 lines
  • Estimated time of completion: 30 min
The whys and wherefores:
 
  • Deserializacja danych
  • Korzystanie z biblioteki JSON
  • Deserializacja zagnieżdżonych obiektów
  • Reprezentacja klas na podstawie danych otrzymanych przez API
[{"model":"authorization.user","pk":1,"fields":{"password":"pbkdf2_sha256$120000$gvEBNiCeTrYa0$5C+NiCeTrYsha1PHogqvXNiCeTrY0CRSLYYAA90=","last_login":"1970-01-01T00:00:00.000Z","is_superuser":false,"username":"commander","first_name":"Иван","last_name":"Иванович","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"eclss":["add","modify","view"]},{"communication":["add","modify","view"]},{"medical":["add","modify","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":2,"fields":{"password":"pbkdf2_sha256$120000$eUNiCeTrYHoh$X32NiCeTrYZOWFdBcVT1l3NiCeTrY4WJVhr+cKg=","last_login":null,"is_superuser":false,"username":"executive-officer","first_name":"José","last_name":"Jiménez","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"eclss":["add","modify","view"]},{"communication":["add","modify","view"]},{"medical":["add","modify","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":3,"fields":{"password":"pbkdf2_sha256$120000$3G0RNiCeTrYlaV1$mVb62WNiCeTrYQ9aYzTsSh74NiCeTrY2+c9/M=","last_login":"1970-01-01T00:00:00.000Z","is_superuser":false,"username":"crew-medical-officer","first_name":"Melissa","last_name":"Lewis","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"communication":["add","view"]},{"medical":["add","modify","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":4,"fields":{"password":"pbkdf2_sha256$120000$QmSNiCeTrYBv$Nt1jhVyacNiCeTrYSuKzJ//WdyjlNiCeTrYYZ3sB1r0g=","last_login":null,"is_superuser":false,"username":"science-data-officer","first_name":"Mark","last_name":"Watney","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"communication":["add","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":5,"fields":{"password":"pbkdf2_sha256$120000$bxS4dNiCeTrY1n$Y8NiCeTrYRMa5bNJhTFjNiCeTrYp5swZni2RQbs=","last_login":null,"is_superuser":false,"username":"communication-officer","first_name":"Matt","last_name":"Kowalski","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"communication":["add","modify","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":6,"fields":{"password":"pbkdf2_sha256$120000$aXNiCeTrY$UfCJrBh/qhXohNiCeTrYH8nsdANiCeTrYnShs9M/c=","last_login":null,"is_superuser":false,"username":"eclss-officer","first_name":"Harry","last_name":"Stamper","email":"","is_staff":true,"is_active":true,"date_joined":"1970-01-01T00:00:00.000Z","groups":[1],"user_permissions":[{"communication":["add","view"]},{"eclss":["add","modify","view"]},{"science":["add","modify","view"]}]}}]