Ошибка десериализации json python

Нужно реализовать key-value хранилище, при получении значения по ключу выдает ошибку :

Traceback (most recent call last):

File "storage.py", line 36, in <module>
json_data = json.load(f)  

File "/usr/lib/python3.6/json/__init__.py", line 299, in load
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)

File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)

File "/usr/lib/python3.6/json/decoder.py", line 342, in decode
raise JSONDecodeError("Extra data", s, end)

json.decoder.JSONDecodeError: Extra data: line 1 column 5 (char 4)

Программа:

import os, sys, argparse, json, tempfile

def get_key(d, key):
    for v, k in d.keys():
        if k == key:
            return v


parser = argparse.ArgumentParser()
parser.add_argument('--key', dest = 'key_name')
parser.add_argument('--val', dest = 'value')

args = parser.parse_args()

my_dict = {}
storage_path = os.path.join(tempfile.gettempdir(), 'storage.data')
if os.path.isfile(u'/tmp/storage.data') == False:            
    if args.key_name != None:
        if args.value != None: 
            with open(storage_path, 'w') as f:
                json_dp = json.dump(my_dict.update([(args.key_name,args.value)]),f)
    else:
        print(None)
else:
    if args.key_name != None: 
        if args.value != None:  
            with open(storage_path, 'a') as f: 
                json_dp = json.dump(my_dict.update([(args.key_name,args.value)]), f) 

        else: 
            with open(storage_path, 'r') as f: 
               json_data = json.load(f)   
               print(get_key(json_data, args.key_name))                       

Нужно реализовать key-value хранилище, при получении значения по ключу выдает ошибку :

Traceback (most recent call last):

File "storage.py", line 36, in <module>
json_data = json.load(f)  

File "/usr/lib/python3.6/json/__init__.py", line 299, in load
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)

File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)

File "/usr/lib/python3.6/json/decoder.py", line 342, in decode
raise JSONDecodeError("Extra data", s, end)

json.decoder.JSONDecodeError: Extra data: line 1 column 5 (char 4)

Программа:

import os, sys, argparse, json, tempfile

def get_key(d, key):
    for v, k in d.keys():
        if k == key:
            return v


parser = argparse.ArgumentParser()
parser.add_argument('--key', dest = 'key_name')
parser.add_argument('--val', dest = 'value')

args = parser.parse_args()

my_dict = {}
storage_path = os.path.join(tempfile.gettempdir(), 'storage.data')
if os.path.isfile(u'/tmp/storage.data') == False:            
    if args.key_name != None:
        if args.value != None: 
            with open(storage_path, 'w') as f:
                json_dp = json.dump(my_dict.update([(args.key_name,args.value)]),f)
    else:
        print(None)
else:
    if args.key_name != None: 
        if args.value != None:  
            with open(storage_path, 'a') as f: 
                json_dp = json.dump(my_dict.update([(args.key_name,args.value)]), f) 

        else: 
            with open(storage_path, 'r') as f: 
               json_data = json.load(f)   
               print(get_key(json_data, args.key_name))                       

{
[
{
«id»:1,
«Рус. Название»:»Храм Василия Блаженного»,
«Англ. Название»:»St. Basil’s Cathedral»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Храм_Василия_Блаженного»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Saint_Basil%27s_Cathedral»,
«Адрес»:»Красная площадь, 7″,
«Координаты»:»55.75253278804399,37.62309622086314″
},
{
«id»:2,
«Рус. Название»:»Памятник Минину и Пожарскому»,
«Англ. Название»:»Monument to Minin and Pozharsky»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Памятник_Минину_и_По…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Monument_to_Minin_an…»,
«Адрес»:»Красная площадь, 7″,
«Координаты»:»55.75278691817848,37.622645609748645″
},
{
«id»:3,
«Рус. Название»:»Спасская башня»,
«Англ. Название»:»Spasskaya Tower»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Спасская_башня»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Spasskaya_Tower»,
«Адрес»:»Тверской район, Кремль, 12″,
«Координаты»:»55.75263262508084,37.62150298870831″
},
{
«id»:4,
«Рус. Название»:»Лобное место на Красной Площади»,
«Англ. Название»:»Lobnoye mesto»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Лобное_место»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Lobnoye_Mesto»,
«Адрес»:»Красная площадь»,
«Координаты»:»55.753216515339,37.62251149929791″
},
{
«id»:5,
«Рус. Название»:»Мавзолей Ленина»,
«Англ. Название»:»Lenin’s Mausoleum»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Мавзолей_Ленина»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Lenin%27s_Mausoleum»,
«Адрес»:»Красная площадь»,
«Координаты»:»55.7537349753831,37.61993877843665″
},
{
«id»:6,
«Рус. Название»:»ГУМ»,
«Англ. Название»:»GUM»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/ГУМ»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/GUM_(department_store)»,
«Адрес»:»Красная площадь, 3″,
«Координаты»:»55.75452047240065,37.621000934165615″
},
{
«id»:8,
«Рус. Название»:»Столовая № 57 «,
«Англ. Название»:»Dining room No. 57″,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»»,
«Англ. Wiki»:»»,
«Адрес»:»Красная площадь, 3″,
«Координаты»:»55.754176181320865,37.623260171748136″
},
{
«id»:9,
«Рус. Название»:»Казанский собор»,
«Англ. Название»:»Kazan Cathedral»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Казанский_собор_(Москва)»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Kazan_Cathedral,_Moscow»,
«Адрес»:»Никольская улица, 3″,
«Координаты»:»55.75539606348181,37.61918185441502″
},
{
«id»:10,
«Рус. Название»:»Исторический музей «,
«Англ. Название»:»Historical Museum»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Государственный_исто…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/State_Historical_Museum»,
«Адрес»:»Красная площадь, 1″,
«Координаты»:»55.755393354751924,37.61799095361239″
},
{
«id»:11,
«Рус. Название»:»Воскресенские ворота»,
«Англ. Название»:»Resurrection Gate»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Воскресенские_ворота»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Iberian_Gate_and_Chapel»,
«Адрес»:»проезд Воскресенские Ворота, 1А»,
«Координаты»:»55.75566505507187,37.618045211312165″
},
{
«id»:12,
«Рус. Название»:»Нулевой километр»,
«Англ. Название»:»Kilometre Zero»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»»,
«Англ. Wiki»:»»,
«Адрес»:»проезд Воскресенские Ворота»,
«Координаты»:»55.75582236123602,37.61769652414025″
},
{
«id»:13,
«Рус. Название»:»Александровский сад «,
«Англ. Название»:»Alexander Garden»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Александровский_сад_…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Alexander_Garden»,
«Адрес»:»Александровский сад»,
«Координаты»:»55.75211610144802,37.613610828416256″
},
{
«id»:15,
«Рус. Название»:»Охотный ряд «,
«Англ. Название»:»Okhotny Ryad»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Охотный_Ряд»,
«Англ. Wiki»:»»,
«Адрес»:»пл. Манежная, д. 1, стр. 2″,
«Координаты»:»55.755758,37.61468″
}
]
}

Are you looking for a simple way to fix Object of type ARandomClass is not JSON serializable where ARandomClass is a built-in Python object or a custom class you’ve defined on your own. You’re probably trying to encode a Python object to a JSON formatted string. In this article, we will show you a few cases where this error would happen, and how to avoid it in the future.

“Object of type is not JSON serializable” is a common error message in Python. It basically says that the built-in json module cannot transform the current object type into a JSON-formatted string.

The built-in json module of Python can only handle basic Python objects such as dictionaries, lists, strings, numbers, None, etc. Below is a table of how native Python objects are translated to JSON.

Python JSON
dict object
listtuple array
str string
intlongfloat number
True true
False false
None null

Now you have two options to handle Object of type is not JSON serializable

  • Create a custom JSON decoder for the object type of your choice. (https://pynative.com/make-python-class-json-serializable/)
  • Fix your script so that it returns one of the Python primitives mentioned above.

Decode bytes to string before serialization

If you’re seeing something like “Object of type bytes is not JSON serializable”, it means that json module has failed to convert a bytes object into JSON formatted string. What you have to do is either removing string.encode() calls or adding string.decode() to make sure that json.dumps() only takes in raw string as input. Consider the code below:

import json bytes_object = 'mystring'.encode('utf-8') json_str = json.dumps({'message': bytes_object})

Code language: Python (python)

In the code snippet, we’ve tried passing a bytes object into json.dumps, which will eventually result in “TypeError: Object of type ‘bytes’ is not JSON serializable”. In order to get rid of it, we can either remove the string.encode() call or add a string.decode() call before actually serialize the object.

Writing a custom JSON encoder

The json module relies on its “encoders” to transform native Python objects to strings. By default, every single object is passed onto an instance of json.JSONEncoder class which must have encode(), default() and iterencode() methods to do the serialization.

In order to serialize your own object type, you would have to overload json.JSONEncoder and implement custom encode(), default() and iterencode() methods. Let’s look at an example where we define a new class and encode it to JSON:

import json from json import JSONEncoder class CustomData: def __init__(self, name, value): self.name = name self.value = value class CustomEncoder(json.JSONEncoder): def default(self, o): return o.__dict__ # Create an instance and serialize it with our new encoder the_value = CustomData("Paris", 120) jsonized = json.dumps(the_value, indent=4, cls=CustomEncoder) print(jsonized)

We’ll see something like this :

{ "name": "Paris", "value": 120, }

Code language: JSON / JSON with Comments (json)

Use jsonpickle

If you need a quick and dirty solution without involving in too much coding, then jsonpickle is made for you.

jsonpickle is a Python library for serialization and deserialization of complex Python objects to and from JSON.

jsonpickle is built so that it allows more complex data structure to be serialized right out of the box. Most of the time, jsonpickle can take almost any Python object and turn the object into JSON. Additionally, it can reconstitute the object back into Python.

Under the hood, jsonpickle uses the same toolset you’re already familiar with : stdlib’s json, simplejson, and demjson. By default, it uses built-in json library from stdlib, but you can choose another JSON backend as well as roll your own backend if you want.

Below is an example of how we would serialize an object with jsonpickle:

import jsonpickle class Thing(object): def __init__(self, name): self.name = name obj = Thing('Awesome') frozen = jsonpickle.encode(obj)

We hope that the information above helped you fix “Object of type is not JSON serializable” error message in Python. We’ve written a few other guides on troubleshooting other common Python problems such as Fix Python Unresolved Import in VSCode, Fix “Max retries exceeded with URL” error in Python requests library and Fix locale.Error: unsupported locale setting in Python.

If you have any suggestion, please feel free to leave a comment below.

Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)

Сериализация и десериализация объектов Python является важным аспектом любой нетривиальной программы. Если в Python вы сохраняете что-то в файле, если вы читаете файл конфигурации или отвечаете на HTTP-запрос, вы выполняете сериализацию и десериализацию объектов.

В некотором смысле, сериализация и десериализация — самые скучные вещи в мире. Кто заботится обо всех форматах и протоколах? Вы просто хотите сохранить или стримить некоторые объекты Python и вернуть их позже.

Это очень здоровый способ взглянуть на мир на концептуальном уровне. Но на прагматическом уровне, какая схема сериализации, формат или протокол, которые вы выбираете, могут определять, насколько быстро работает ваша программа, насколько она безопасна, насколько свободно вы должны поддерживать свое состояние и насколько хорошо вы собираетесь взаимодействовать с другими системы.

Причина в том, что существует так много вариантов, что разные обстоятельства требуют разных решений. Нет серебренной поли. В этом учебнике из двух частей я расскажу о преимуществах и недостатках самых успешных схем сериализации и десериализации, покажу, как их использовать, и дам рекомендации по выбору между ними, когда вы сталкиваетесь с конкретным вариантом использования.

Пример выполнения

В следующих разделах я буду сериализовать и десериализовать одни и те же графы объектов Python, используя различные сериализаторы. Чтобы избежать повторения, я определяю эти графы объектов здесь.

Простой Graph объект

Простой graph объект — это словарь, который содержит список целых чисел, строку, float, логическое и None.

1
simple = dict(int_list=[1, 2, 3],
2

3
              text='string',
4

5
              number=3.44,
6

7
              boolean=True,
8

9
              none=None) 

Сложный Graph объект

Граф сложного объекта также является словарем, но он содержит объект datetime и пользовательский экземпляр класса с атрибутом self.simple, который устанавливается на простой граф объектов.

1
from datetime import datetime
2

3

4

5
class A(object):
6

7
    def __init__(self, simple):
8

9
        self.simple = simple        
10

11
    def __eq__(self, other):
12

13
        if not hasattr(other, 'simple'):
14

15
            return False
16

17
        return self.simple == other.simple
18

19
    def __ne__(self, other):
20

21
        if not hasattr(other, 'simple'):
22

23
            return True
24

25
        return self.simple != other.simple
26

27

28

29
complex = dict(a=A(simple), when=datetime(2016, 3, 7))

Pickle

Pickle является основным продуктом. Это собственный формат сериализации объекта Python. Интерфейс pickle обеспечивает четыре метода: dump, dumps, load, и loads. Метод dump() сериализует в открытый файл (файл-подобный объект). Метод dumps() сериализует в строку. Метод load() десериализует из открытого файлового объекта. Метод loads() десериализует из строки.

Pickle поддерживает по умолчанию текстовый протокол, но имеет также двоичный протокол, который более эффективен, но не читается человеком (полезно при отладке).

Вот как вы распиливаете граф объекта Python на строку и в файл, используя оба протокола.

1
import cPickle as pickle
2

3

4

5
pickle.dumps(simple)
6

7
"(dp1nS'text'np2nS'string'np3nsS'none'np4nNsS'boolean'np5nI01nsS'number'np6nF3.4399999999999999nsS'int_list'np7n(lp8nI1naI2naI3nas."
8

9

10

11
pickle.dumps(simple, protocol=pickle.HIGHEST_PROTOCOL)
12

13
'x80x02}qx01(Ux04textqx02Ux06stringqx03Ux04noneqx04NUx07booleanx88Ux06numberqx05G@x0bx85x1exb8Qxebx85Ux08int_list]qx06(Kx01Kx02Kx03eu.'

Бинарное представление может показаться большим, но это иллюзия из-за его представления. При сбрасывании в файл текстовый протокол составляет 130 байт, а бинарный протокол — всего 85 байт.

1
pickle.dump(simple, open('simple1.pkl', 'w'))
2

3
pickle.dump(simple, open('simple2.pkl', 'wb'), protocol=pickle.HIGHEST_PROTOCOL)
4

5

6

7
ls -la sim*.*
8

9
-rw-r--r--  1 gigi  staff  130 Mar  9 02:42 simple1.pkl
10

11
-rw-r--r--  1 gigi  staff   85 Mar  9 02:43 simple2.pkl

Выделение из строки так же просто, как:

1
x = pickle.loads("(dp1nS'text'np2nS'string'np3nsS'none'np4nNsS'boolean'np5nI01nsS'number'np6nF3.4399999999999999nsS'int_list'np7n(lp8nI1naI2naI3nas.")
2

3
assert x == simple
4

5

6

7
x = pickle.loads('x80x02}qx01(Ux04textqx02Ux06stringqx03Ux04noneqx04NUx07booleanx88Ux06numberqx05G@x0bx85x1exb8Qxebx85Ux08int_list]qx06(Kx01Kx02Kx03eu.')
8

9
assert x == simple

Обратите внимание, что pickle может автоматически определять протокол. Нет необходимости указывать протокол даже для двоичного.

Получить из файла так же просто. Вам просто нужно предоставить открытый файл.

1
x = pickle.load(open('simple1.pkl'))
2

3
assert x == simple
4

5

6

7
x = pickle.load(open('simple2.pkl'))
8

9
assert x == simple
10

11

12

13
x = pickle.load(open('simple2.pkl', 'rb'))
14

15
assert x == simple

Согласно документации, вы должны открыть бинарные pickles, используя режим «rb», но, как видите, это работает в любом случае.

Посмотрим, как pickle справляется со сложным графом объектов.

1
pickle.dumps(complex)
2

3
"(dp1nS'a'nccopy_regn_reconstructornp2n(c__main__nAnp3nc__builtin__nobjectnp4nNtRp5n(dp6nS'simple'np7n(dp8nS'text'np9nS'string'np10nsS'none'np11nNsS'boolean'np12nI01nsS'number'np13nF3.4399999999999999nsS'int_list'np14n(lp15nI1naI2naI3nassbsS'when'np16ncdatetimendatetimenp17n(S'x07xe0x03x07x00x00x00x00x00x00'ntRp18ns."
4

5

6

7
pickle.dumps(complex, protocol=pickle.HIGHEST_PROTOCOL)
8

9
'x80x02}qx01(Ux01ac__main__nAnqx02)x81qx03}qx04Ux06simpleqx05}qx06(Ux04textqx07Ux06stringqx08Ux04noneqtNUx07booleanx88Ux06numberqnG@x0bx85x1exb8Qxebx85Ux08int_list]qx0b(Kx01Kx02Kx03eusbUx04whenqx0ccdatetimendatetimenqrUnx07xe0x03x07x00x00x00x00x00x00x85Rqx0eu.'
10

11

12

13
pickle.dump(complex, open('complex1.pkl', 'w'))
14

15
pickle.dump(complex, open('complex2.pkl', 'wb'), protocol=pickle.HIGHEST_PROTOCOL)
16

17

18

19
ls -la comp*.*
20

21
-rw-r--r--  1 gigi  staff  327 Mar  9 02:58 complex1.pkl
22

23
-rw-r--r--  1 gigi  staff  171 Mar  9 02:58 complex2.pkl

Эффективность двоичного протокола еще больше при использовании сложных объектов.

JSON (JavaScript Object Notation) является частью стандартной библиотеки Python с Python 2.5. На данный момент я буду считать его родным. Это текстовый формат и является неофициальным королем сети, поскольку идет сериализация объектов. Его система типов, естественно, моделирует JavaScript, поэтому она довольно ограничена.

Давайте сериализуем и десериализируем простые и сложные графы объектов и выясним, что происходит. Интерфейс почти идентичен интерфейсу pickle. У вас есть функции dump(), dumps(), load(), и loads(). Но нет выбора протоколов, и есть много дополнительных аргументов для управления процессом. Давайте начнем просто, сбросив простой граф объектов без каких-либо специальных аргументов:

1
import json
2

3
print json.dumps(simple)
4

5
{"text": "string", "none": null, "boolean": true, "number": 3.44, "int_list": [1, 2, 3]}

Результат выглядит довольно читаемым, но нет отступов. Для более крупного графа объектов это может быть проблемой. Давайте отпечатаем вывод:

1
print json.dumps(simple, indent=4)
2

3
{
4

5
    "text": "string",
6

7
    "none": null,
8

9
    "boolean": true,
10

11
    "number": 3.44,
12

13
    "int_list": [
14

15
        1,
16

17
        2,
18

19
        3
20

21
    ]
22

23
}

Это выглядит намного лучше. Перейдем к графу сложных объектов.

1
json.dumps(complex)
2

3
---------------------------------------------------------------------------
4

5
TypeError                                 Traceback (most recent call last)
6

7
<ipython-input-19-1be2d89d5d0d> in <module>()
8

9
----> 1 json.dumps(complex)
10

11

12

13
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.pyc in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, encoding, default, sort_keys, **kw)
14

15
    241         cls is None and indent is None and separators is None and
16

17
    242         encoding == 'utf-8' and default is None and not sort_keys and not kw):
18

19
--> 243         return _default_encoder.encode(obj)
20

21
    244     if cls is None:
22

23
    245         cls = JSONEncoder
24

25

26

27
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in encode(self, o)
28

29
    205         # exceptions aren't as detailed.  The list call should be roughly

30

31
    206         # equivalent to the PySequence_Fast that ''.join() would do.

32

33
--> 207         chunks = self.iterencode(o, _one_shot=True)
34

35
    208         if not isinstance(chunks, (list, tuple)):
36

37
    209             chunks = list(chunks)
38

39

40

41
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in iterencode(self, o, _one_shot)
42

43
    268                 self.key_separator, self.item_separator, self.sort_keys,
44

45
    269                 self.skipkeys, _one_shot)
46

47
--> 270         return _iterencode(o, 0)
48

49
    271
50

51
    272 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
52

53

54

55
/usr/local/Cellar/python/2.7.10/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.pyc in default(self, o)
56

57
    182
58

59
    183         """

60


61
--> 184         raise TypeError(repr(o) + " is not JSON serializable")

62


63
    185

64


65
    186     def encode(self, o):

66


67


68


69
TypeError: <__main__.A object at 0x10f367cd0> is not JSON serializable

Вау! Это не выглядит хорошо. Что случилось? Сообщение об ошибке состоит в том, что объект A не является сериализуемым JSON. Помните, что JSON имеет очень ограниченную систему типов и не может автоматически сериализовать пользовательские классы. Способ обращения к ней заключается в подклассе класса JSONEncoder, используемого модулем json, и реализации default(), которая вызывается всякий раз, когда кодер JSON запускается в объект, который он не может сериализовать.

Задача пользовательского кодировщика состоит в том, чтобы преобразовать его в граф объектов Python, который кодировщик JSON способен кодировать. В этом случае у нас есть два объекта, для которых требуется специальная кодировка: объект datetime и класс A. Следующий кодер выполняет задание. Каждый специальный объект преобразуется в dict, где ключ — это имя типа, окруженного dunders (двойные подчеркивания). Это будет важно для декодирования.

1
from datetime import datetime
2

3
import json
4

5

6

7

8

9
class CustomEncoder(json.JSONEncoder):
10

11
     def default(self, o):
12

13
         if isinstance(o, datetime):
14

15
             return {'__datetime__': o.replace(microsecond=0).isoformat()}
16

17
         return {'__{}__'.format(o.__class__.__name__): o.__dict__}

Давайте попробуем еще раз с нашим пользовательским кодировщиком:

1
serialized = json.dumps(complex, indent=4, cls=CustomEncoder)
2

3
print serialized
4

5

6

7
{
8

9
    "a": {
10

11
        "__A__": {
12

13
            "simple": {
14

15
    "text": "string",
16

17
    "none": null,
18

19
    "boolean": true,
20

21
    "number": 3.44,
22

23
    "int_list": [
24

25
        1,
26

27
        2,
28

29
        3
30

31
    ]
32

33
            }
34

35
            }
36

37
    },
38

39
    "when": {
40

41
        "__datetime__": "2016-03-07T00:00:00"
42

43
            }
44

45
}

Это прекрасно. Граф комплексного объекта был сериализован правильно, а исходная информация о типе компонентов была сохранена с помощью ключей: «__A__» и «__datetime__». Если вы используете dunders для ваших имен, вам нужно придумать другое соглашение для обозначения специальных типов.

Давайте расшифруем граф сложных объектов.

1
> deserialized = json.loads(serialized)
2

3
> deserialized == complex
4

5
False

Хм, десериализация работала (без ошибок), но она отличается от первоначального графа комплексного объекта, который мы сериализуем. Что-то не так. Давайте рассмотрим граф десериализованного объекта. Я использую функцию pprint модуля pprint для печати.

1
> from pprint import pprint
2

3
> pprint(deserialized)
4

5
{u'a': {u'__A__': {u'simple': {u'boolean': True,
6

7
                               u'int_list': [1, 2, 3],
8

9
                               u'none': None,
10

11
                               u'number': 3.44,
12

13
                               u'text': u'string'}}},
14

15
 u'when': {u'__datetime__': u'2016-03-07T00:00:00'}}

Хорошо. Проблема в том, что модуль json ничего не знает о классе A или даже стандартном объекте datetime. Он просто десериализует все по умолчанию объекту Python, который соответствует его системе типов. Чтобы вернуться к богатому графику объектов Python, вам потребуется собственное декодирование.

Нет необходимости в подклассе пользовательского декодера. Функции load() и loads() предоставляют параметр «object_hook», который позволяет вам предоставить настраиваемую функцию, которая преобразует dicts в объекты.

1
def decode_object(o):
2

3
    if '__A__' in o:
4

5
        a = A()
6

7
        a.__dict__.update(o['__A__'])
8

9
        return a
10

11
    elif '__datetime__' in o:
12

13
        return datetime.strptime(o['__datetime__'], '%Y-%m-%dT%H:%M:%S')        
14

15
    return o

Давайте расшифруем с помощью функции decode_object() в качестве параметра параметра loads() object_hook.

1
> deserialized = json.loads(serialized, object_hook=decode_object)
2

3
> print deserialized
4

5
{u'a': <__main__.A object at 0x10d984790>, u'when': datetime.datetime(2016, 3, 7, 0, 0)}
6

7

8

9
> deserialized == complex
10

11
True

Заключение

В первой части этого учебника вы узнали об общей концепции сериализации и десериализации объектов Python и изучили входы и выходы сериализации объектов Python с использованием Pickle и JSON.

Во второй части вы узнаете о YAML, проблемах производительности и безопасности и получите быстрый обзор дополнительных схем сериализации.

Файлы используются программами для долговременного хранения информации, как необходимой для собственной работы (например, настройки), так и полученной во время ее исполнения (результаты вычислений и т.д.). Подавляющее большинство программ сегодня в том или ином виде используют файлы, сохраняя результаты работы между сеансами запуска.

Содержание

  • Файлы и файловая система

    • Свойства файла

    • Путь к файлу: абсолютный и относительный

    • Операции с файлами

    • Виды файлов

  • Файловый объект в Python

  • Работа с файлами в Python

    • Основные свойства и методы

    • Простое чтение и запись

    • Чтение файла целиком

  • Сериализация и десериализация

    • Модуль pickle

    • Популярные форматы файлов

      • CSV

      • JSON

8.1.1. Файлы и файловая система¶

Файл (англ. File) — именованная область данных на носителе информации.

Файлы хранятся в файловой системе — каталоге, определяющим способ организации, хранения и именования данных, а также задающем ограничения на формат и доступ к данным. На сегодняшний день наиболее популярными являются древовидные каталоги (также директории или папки) — файлы, содержащие записи о входящих в них файлах (Рисунок 8.1.1).

_images/08_01_01.png

Рисунок 8.1.1 — Пример древовидной организации файловой системы в ОС Windows 6.

Файловая система связывает носитель информации с одной стороны и программный интерфейс для доступа к файлам — с другой. Когда прикладная программа обращается к файлу, она не имеет никакого представления о том, каким образом расположена информация в конкретном файле, так же как и на каком физическом типе носителя (CD, жестком диске, магнитной ленте, блоке флеш-памяти или другом) он записан. Все, что знает программа — это имя файла, его размер и атрибуты (получая их от драйвера файловой системы). Именно файловая система устанавливает, где и как будет записан файл на физическом носителе (например, жестком диске) (Рисунок 8.1.2).

_images/08_01_02.png

Рисунок 8.1.2 — Файловая система предоставляет интерфейс доступа к файлам для операционной системы 7

8.1.1.1. Свойства файла¶

Файл может обладать различным набором свойств в зависимости от файловой системы.

В большинстве файловых систем файл имеет следующие свойства:

  • имя и расширение (как правило, называемые просто именем вместе): например, моя_программа.py;

  • дата/время (могут быть предусмотрены маркеры создания, модификации и последнего доступа);

  • владелец;

  • атрибуты (скрытый, системный и др.) и права доступа.

Имя файла имеет определенные ограничения в зависимости от файловой и операционной системы, в частности, допустимые знаки и длину наименования. Расширение указывается после имени через точку, имея назначение, в основном, для ОС Windows, где определяет приложение для запуска файла.

8.1.1.2. Путь к файлу: абсолютный и относительный¶

Для того, чтобы найти файл в файловой системе необходимо знать к нему путь — узлы дерева файловой системы, которые нужно пройти, чтобы до него добраться.

В операционных системах UNIX разделительным знаком при записи пути является /, в Windows — : эти знаки служат для разделения названия каталогов, составляющих путь к файлу.

Путь может быть:

  • абсолютным (полным): указывает на одно и то же место в файловой системе вне зависимости от текущей рабочей директории или других обстоятельств;

  • относительным: путь по отношению к текущему рабочему каталогу пользователя или активных приложений.

Примеры путей для ОС Windows и UNIX:

  • ОС Windows:

    • абсолютный: C:userpythonexample1.py;

    • относительный: example1.py если текущий каталог C:userpython;

    • относительный: pythonexample1.py если текущий каталог C:user;

  • ОС UNIX:

    • абсолютный: /home/user/python/example1.py;

    • относительный: example1.py если текущий каталог /home/user/python/;

    • относительный: user/python/example1.py если текущий каталог /home/.

8.1.1.3. Операции с файлами¶

Все операции с файлами можно подразделить на 2 группы:

  • связанные с его открытием: открытие, закрытие файла, запись, чтение, перемещение по файлу и др.

  • выполняющиеся без его открытия: работа с файлом как элементом файловой системы — переименование, копирование, получение атрибутов и др.

При открытии файла, как правило, указываются:

  • имя файла;

  • права доступа

после чего операционная система возвращает специальный дескриптор файла (идентификатор), однозначно определяющий, с каким файлом далее будут выполняться операции. После открытия доступен файловый указатель — число, определяющее позицию относительно начала файла.

8.1.1.4. Виды файлов¶

По способу организации файлы делятся на файлы с последовательным и произвольным доступом (Рисунок 8.1.3, Таблица 8.1.1).

_images/08_01_03.png

Рисунок 8.1.3 — Файлы с последовательным и произвольным доступом 8

Таблица 8.1.1 — Разница в доступе к файлам с последовательным и произвольным доступом

Характеристика

Последовательный доступ

Произвольный доступ

1

Метафора

_images/08_01_01_i.png

_images/08_01_02_i.png

2

Как хранят информацию?

В неструктурированном виде (текстовом)

В структурированном виде (двоичном)

2

Поиск

Для поиска или обращения к определенному участку в файле необходимо его последовательно прочитать с начала

Разрешен непосредственный переход к любой из записи

3

Можно править в текстовом редакторе?

Да

Нет, необходима специальная программа

4

Плюсы

Компактность

Скорость доступа

5

Основное применение

Файлы конфигурации, документы

Файлы баз данных и их производные

6

Пример

Текстовый файл (*.txt и др.)

База данных (*.mdb и др.)

8.1.2. Файловый объект в Python¶

В Python работа с файлами осуществляется через специальный абстрактный файловый объект. В зависимости от способа создания такого объекта, он может быть привязан как к физическому файлу на диске, так и другому устройству, поддерживающему схожие операции (стандартный ввод/вывод и пр.).

Стандартный способ создания файлового объекта — функция open():

open(file, mode=‘r’, buffering=— 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

Открывает файл и возвращает файловый объект. Если файл не может быть открыт, возбуждается исключение OSError и его потомки (FileNotFoundError и др.).

Некоторые из параметров:

Параметры
  • file – путь к файлу (например, строка) или файловый дескриптор;

  • mode

    режим открытия файла:

    Символ

    Описание

    'r'

    Открыть для чтения (по умолчанию)

    'w'

    Открыть для записи (если файл существует, то очищается)

    'x'

    Открыть для создания с эксклюзивными правами (ошибка, если файл существует)

    'a'

    Открыть для добавления (если файл существует)

    '+'

    Открыть для чтения и записи

    't'

    Текстовый режим (по умолчанию)

    'b'

    Двоичный режим

    Двоичный/текстовый режимы могут быть скомбинированы с другими: например, режим 'rb' позволит открыть на чтение бинарный файл;

  • encoding – наименование кодировки, используемой при чтении/записи файла (например, 'utf-8'); параметр имеет смысл только для текстового режима.

Предупреждение

Кодировка файла

При открытии файла Python по умолчанию использует кодировку, предпочитаемую операционной системой. Для определения кодировки по умолчанию выполните код:

import locale
locale.getpreferredencoding(False)  # 'cp1251' на российской сборке ОС Windows

Старайтесь указывать кодировку файла явно, например, encoding="utf-8", особенно если есть вероятность работы программы на различных ОС.

Открытие файлов связано с потреблением/резервированием ресурсов, поэтому после выполнения необходимых операций его следует закрыть.

class file
close()

Закрывает файл. После этого работа с файлом невозможна (чтение, запись и др.).

Общий цикл работы с файлом может выглядеть следующим образом (Листинг 8.1.1).

Листинг 8.1.1 — Пример цикла работы с файлом | скачать

# При работе с файлами всегда целесообразно использовать исключения

# 1. Стандартный способ открытия файла с обработкой исключений
fh = None
try:
    fh = open("data.txt", encoding="utf-8")
    #
    # Чтение и запись файла
    #
except Exception as e:
    print("Ошибка при работе с файлом:", e)
finally:
    if fh:  # Если файл не открылся, значит 'fh' == None и закрывать его не нужно
        fh.close()

# 2. Для упрощения кода по выделению и высвобождению ресурсов в Python
#    предусмотрены специальные объекты - менеджеры контекста,
#    которые могут самостоятельно следить за использованием ресурсов
#
#    Наиболее часто менеджеры контекста вызываются с использованием ключевого слова with

try:
    with open("data2.txt", encoding="utf-8") as fh:
        #
        # Чтение и запись файла
        #
except Exception as e:
    print("Ошибка при работе с файлом:", e)

Примечание

В большинстве примеров данной темы подробная обработка исключений опущена для того, чтобы сосредоточиться на демонстрации конкретных возможностей.

При самостоятельном написании программ не следует забывать добавлять обработку исключений — программа должна адекватно реагировать на возможные нестандартные ситуации (используемые файлы может изменить пользователь или другая программа).

8.1.3. Работа с файлами в Python¶

8.1.3.1. Основные свойства и методы¶

Файловый объект предоставляет ряд свойств и методов для работы с файлами. Большинство методов универсально и предполагают работу (чтение/запись) со строками в указанной кодировке (str для текстовых файлов) или с набором байт (bytes для двоичных файлов).

class file
name

Имя файла (если имеется).

mode

Режим, в котором был открыт файл.

encoding

Кодировка, используемая для строковых преобразований в файле.

closed

Возвращает True, если файл закрыт.

close()

Закрывает файл. После этого работа с файлом невозможна (чтение, запись и др.).

read(count)

Читает до count байт из файлового объекта. Если значение count не определено, то читаются все байты, начиная от текущей позиции и до конца.

Если ничего не было прочитано (конец файла), возвращается пустой объект str или bytes.

readinto(ba)

Читает до len(ba) байт в объект ba типа bytearray и возвращает число прочитанных байт (0, если был достигнут конец файла).

Доступен только в двоичном режиме.

readline(count)

Читает следующую строку (до count байт, если значение count определено и число прочитанных байтов было достигнуто раньше, чем встретился символ перевода строки 'n'), включая символ перевода строки 'n'.

readlines(sizehint)

Читает все строки до конца файла и возвращает их в виде списка.

Если значение аргумента sizehint определено, то будет прочитано примерно sizehint байт (если файл поддерживает такую возможность).

write(s)

Записывает в файл объект s типа:

  • str для текстового режима;

  • bytes / bytearray для двоичного режима.

Примечание

Запись в файл возможна и с помощью стандартной функции print(), установив ключевой параметр file в открытый файловый объект.

writelines(seg)

Записывает в файл последовательность объектов типа:

  • str для текстового режима;

  • bytes / bytearray для двоичного режима.

flush()

При работе с файлами Python по умолчанию использует буфер определенного размера. Вызов методов записи приводит к записи в буфер, после чего в определенный момент производится очистка буфера и передача сигнала для записи данных на диск.

Метод flush() вызывает принудительное очистку буфера. В большинстве случаев вызывать его напрямую не нужно.

close()

Закрывает файл. После этого работа с файлом невозможна (чтение, запись и др.).

8.1.3.2. Простое чтение и запись¶

В Листинге 8.1.2 приведен пример работы с двоичным файлом.

Листинг 8.1.2 — Пример работы с двоичным файлом | скачать

human = dict(name="Михаил Толстых", age=35, weight=90.5)
filename = "08_01_02_data.txt"

# 1. Запись в файл
#    При записи бинарных файлов все данные должны быть преобразованы в тип bytes
#    Удобно выполнить преобразование через метод str.encode()
fh = None
try:
    fh = open(filename, "wb")
    fh.write(bytes(human["name"].encode("utf-8")))
    fh.write(bytes(str(human["age"]).encode("utf-8")))
    fh.write(bytes(str(human["weight"]).encode("utf-8")))
finally:
    if fh:
        fh.close()

# 2. Чтение из файла
#    При чтении бинарных файлов необходимо точно знать, сколько байт
#    прочитать и как их декодировать, используя bytes.decode()
#    При редактировании файла в стороннем редакторе файл может быть не читаем
fh = None
try:
    fh = open(filename, "rb")
    name = fh.read(27).decode("utf-8")
    age = int(fh.read(2).decode("utf-8"))
    weight = float(fh.read(4).decode("utf-8"))
    print(name, age, weight)  # Михаил Толстых 35 90.5
finally:
    if fh:
        fh.close()

В Листинге 8.1.3 приведен пример работы с текстовым файлом.

Листинг 8.1.3 — Пример работы с текстовым файлом | скачать

human = dict(name="Михаил Толстых", age=35, weight=90.5)
filename = "08_01_03_data.txt"

# 1. Запись в файл
#    При записи текстовых файлов все данные должны быть преобразованы в тип str
#    По умолчанию, write() не добавляет перенос строки, однако его можно добавить самостоятельно
fh = None
try:
    fh = open(filename, "w", encoding="utf-8")
    # При добавлении переноса записываемые данные будут на отдельной строке
    fh.write(human["name"] + "n")
    fh.write(str(human["age"]) + "n")
    # Как альтернатива - print() позволяет не переводить в строку и не добавлять перенос вручную
    print(human["weight"], file=fh)
finally:
    if fh:
        fh.close()

# 2. Чтение из файла
#    Для чтения отдельной строки достаточно вызвать метод readline().
#    В конце полученной строки знак переноса - n,
#    который можно убрать, например, методом str.strip()
fh = None
try:
    fh = open(filename, encoding="utf-8")
    # Читаем первые 3 строки и преобразуем при необходимости
    name = fh.readline().strip()
    age = int(fh.readline())
    weight = float(fh.readline())
    print(name, age, weight)  # Михаил Толстых 35 90.5
finally:
    if fh:
        fh.close()

8.1.3.3. Чтение файла целиком¶

Чтение файла, приведенное в Листинге 8.1.3 не подразумевает, что содержимое файла может меняться.

Учитывать такие изменения можно, прочитав файл целиком:

  • в одну строку;

  • в список строк;

  • построчно, пока не достигнут конец файла.

Примеры реализации данных вариантов приведены в Листинге 8.1.4 на примере файла 08_01_04_data.txt:

Содержимое файла 08_01_04_data.txt | скачать

Когда дети будут смотреть на великих учёных так же,
как они смотрят на знаменитых актёров и музыкантов,
человечество совершит большой прорыв.

Брайан Грин
https://ru.wikipedia.org/wiki/Грин,_Брайан_Рэндолф

Листинг 8.1.4 — Различные способы чтения текстового файла целиком | скачать

filename = "08_01_04_data.txt"

# В данном примере обработка исключений не приводится, чтобы
# сосредоточить внимание на чтении файлов 

# 1. Чтение из файла (в одну строку)
with open(filename, encoding="utf-8") as fh:
    data = fh.read()
    print(data)

# 2. Чтение из файла (в список)
with open(filename, encoding="utf-8") as fh:
    data = fh.readlines()
    print(data)

# ['Когда дети будут смотреть на великих учёных так же,n',
#  'как они смотрят на знаменитых актёров и музыкантов,n',
#  'человечество совершит большой прорыв.n',
#  'n',
#  'Брайан Гринn',
#  'https://ru.wikipedia.org/wiki/Грин,_Брайан_Рэндолфn']

# 3. Чтение из файла (построчно)
with open(filename, encoding="utf-8") as fh:
    for line in fh:
        print(line.strip())

8.1.4. Сериализация и десериализация¶

Чтение/запись простых типов (например, чисел или строк) не представляет большого труда, однако с увеличением объема информации появляется необходимость эффективно сохранять/загружать более сложные структуры данных (например, словари). Кроме того, модульная архитектура современного программного обеспечения приводит к необходимости обмена данными между различными модулями, а также между приложениями в целом, для чего необходимо иметь возможность удобно обмениваться данными.

Сериализация — процесс перевода какой-либо структуры данных в последовательность битов. Десериализация — обратный процесс.

Чаще всего сериализация используется для сохранения объектов в файлы или передачи их по сети.

8.1.4.1. Модуль pickle

Одним из способов, позволяющих сериализовать/десериализовать данные в Python, является использование стандартного модуля pickle, при помощи которого можно сохранять любой объект Python в двоичном файле, а затем извлекать его обратно.

Основные функции модуля:

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

Сериализует объект obj и записывает его в файл file.

Некоторые из параметров:

Параметры
  • obj – объект для записи;

  • file – файловый объект;

  • protocol – версия формата pickle.

pickle.load(file, *, fix_imports=True, encoding=‘ASCII’, errors=‘strict’)

Читает и десериализует содержимое файла file, возвращая созданный объект (структуру).

Некоторые из параметров:

Параметры

file – файловый объект.

Пример работы с модулем pickle приведен в Листинге 8.1.5.

Листинг 8.1.5 — Использование pickle для загрузки/сохранения объектов Python | скачать

import pickle

filename = "08_01_05_data.txt"
# список покупок
shoplist = {"фрукты": ["яблоки", "манго"],
            "овощи": ["морковь"],
            "бюджет": 1000}

# Запись в файл
with open(filename, "wb") as fh:
    pickle.dump(shoplist, fh)  # помещаем объект в файл

# Считываем из хранилища
shoplist_2 = []
with open(filename, "rb") as fh:
    shoplist_2 = pickle.load(fh)  # загружаем объект из файла
print(shoplist_2)  # {'бюджет': 1000, 'овощи': ['морковь'], 'фрукты': ['яблоки', 'манго']}

Формат файла, создаваемого pickle, приведен на Рисунке 8.1.4.

_images/08_01_04.png

Рисунок 8.1.4 — Содержимое файла с данными при использовании pickle (в редакторе Notepad++)

Минусы формата pickle:

  • специфичен для Python (не может быть использован, если файл будет читаться с использованием других языков программирования);

  • небезопасен (десериализация готовых конструкций языка может привести к выполнению ненадежного кода).

8.1.4.2. Популярные форматы файлов¶

Большое количество данных в совокупности с их разнородностью привело к появлению специальных форматов файлов, позволяющих хранить различные объемы связанной информации и не привязанных к конкретному языку программирования.

Среди них одними из наиболее популярных являются:

  • CSV (англ. Comma-Separated Values — значения, разделенные запятыми);

  • JSON (англ. JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript;

  • XML (англ. eXtensible Markup Language — расширяемый язык разметки);

  • YAML (англ. YAML Ain’t Markup Language — «YAML — Не язык разметки»);

  • INI (англ. Initialization file — файл инициализации);

  • и др.

Подавляющее большинство форматов поддерживается Python (стандартными или сторонними модулями и пакетами).

8.1.4.2.1. CSV¶

CSV (англ. Comma-Separated Values — значения, разделенные запятыми, 2005 г.) — текстовый формат, предназначенный для представления табличных данных. Каждая строка файла — это одна строка таблицы, где значения отдельных колонок разделяются разделительным символом (англ. delimiter) запятой , и заключаются в кавычки " (Рисунок 8.1.5).

_images/08_01_05.png

Несмотря на наличие стандарта (RFC 4180), на сегодняшний день под CSV, как правило, понимают набор значений, разделенных произвольными разделителями, в произвольной кодировке с произвольными окончаниями строк. Это значительно затрудняет перенос данных из одних программ в другие, несмотря на всю простоту реализации поддержки CSV (так, например, Microsoft Excel не всегда открывает стандартные разделенные запятыми данные).

В Python работа с CSV-файлами поддерживается стандартным модулем csv, предоставляющем следующие основные объекты и функции:

csv.reader(csvfile, dialect=‘excel’, **fmtparams)

Создает и возвращает объект для чтения последовательности из CSV-файла.

Некоторые из параметров:

Параметры
  • csvfile – итерируемый объект, возвращающий строку на каждой итерации (например, файловый объект в текстовом режиме доступа);

  • dialect – диалект CSV (набор специальных параметров);

  • fmtparams – дополнительные настройки (совокупность кавычек, разделителей и т.д.).

csv.writer(csvfile, dialect=‘excel’, **fmtparams)

Создает и возвращает объект для записи последовательности в CSV-файл.

Некоторые из параметров:

Параметры
  • csvfile – любой объект, поддерживающий метод записи write();

  • dialect – аналогично csv.reader();

  • fmtparams – аналогично csv.reader().

class csv.DictReader(csvfile, fieldnames=None, restkey=None, restval=None, dialect=‘excel’, *args, **kwds)

Создает и возвращает объект для чтения данных из CSV-файла как словаря значений.

Некоторые из параметров:

Параметры
  • csvfile – итерируемый объект, возвращающий строку на каждой итерации (например, файловый объект в текстовом режиме доступа);

  • fieldnames – список наименований столбцов (если не задан, используется первая строка файла).

class csv.DictWriter(csvfile, fieldnames, restval=», extrasaction=‘raise’, dialect=‘excel’, *args, **kwds)

Создает и возвращает объект для записи данных как словаря значений в CSV-файл.

Некоторые из параметров:

Параметры
  • csvfile – любой объект, поддерживающий метод записи write();

  • fieldnames – список наименований столбцов.

class csv.Writer
writerow(row)

Записывает последовательность row в CSV-файл.

writerows(rows)

Записывает список последовательностей rows в CSV-файл.

class csv.DictWriter

Записывает в файл заголовки файла, переданные при создании класса.

writerow(row)

Записывает словарь row в CSV-файл.

writerows(rows)

Записывает список словарей rows в CSV-файл.

exception csv.Error

Класс исключения, возбуждаемый при ошибке в работе любой из функций модуля.

Пример использования модуля csv приведен в Листинге 8.1.6 (а-б).

Листинг 8.1.6 (а) — Работа с CSV-файлом в Python (последовательность) | скачать

import csv

filename = "08_01_06_a_data.csv"
# список покупок
shoplist = {"яблоки": [2, 100], "манго": [3, 250], "морковь": [1, 35]}

# Запись в файл
with open(filename, "w", encoding="utf-8", newline="") as fh:
    writer = csv.writer(fh, quoting=csv.QUOTE_ALL)
    writer.writerow(["Наименование", "Вес", "Цена/кг."])  # Заголовки столбца
    for name, values in sorted(shoplist.items()):
        writer.writerow([name, *values])
    writer.writerow(["рис", "4", "70"])  # Допишем произвольную запись

# Содержимое файла
#
# "Наименование","Вес","Цена/кг."
# "манго","3","250"
# "морковь","1","35"
# "яблоки","2","100"
# "рис","4","70"

# Чтение файла
rows = []
with open(filename, "r", encoding="utf-8") as fh:
    reader = csv.reader(fh)
    rows = list(reader)  # reader - итерируемый объект и может быть преобразован в список строк

for row in rows:
    print(row)

# Вывод на экран
#
# ['Наименование', 'Вес', 'Цена/кг.']
# ['манго', '3', '250']
# ['морковь', '1', '35']
# ['яблоки', '2', '100']
# ['рис', '4', '70']

Листинг 8.1.6 (б) — Работа с CSV-файлом в Python (словарь) | скачать

import csv

filename = "08_01_06_b_data.csv"
# список покупок
shoplist = {"яблоки": [2, 100], "манго": [3, 250], "морковь": [1, 35]}

# Запись в файл
with open(filename, "w", encoding="utf-8", newline="") as fh:
    writer = csv.DictWriter(fh, fieldnames=["name", "weight", "price"], quoting=csv.QUOTE_ALL)
    writer.writeheader()  # Записывает заголовки в файл
    for name, values in sorted(shoplist.items()):
        writer.writerow(dict(name=name, weight=values[0], price=values[1]))

# Содержимое файла
#
# "name","weight","price"
# "манго","3","250"
# "морковь","1","35"
# "яблоки","2","100"

# Чтение файла
rows = []
with open(filename, "r", encoding="utf-8") as fh:
    reader = csv.DictReader(fh)
    rows = list(reader)  # reader - итерируемый объект и может быть преобразован в список строк

for row in rows:
    print(row)

# Вывод на экран
#
# {'name': 'манго', 'weight': '3', 'price': '250'}
# {'name': 'морковь', 'weight': '1', 'price': '35'}
# {'name': 'яблоки', 'weight': '2', 'price': '100'}

8.1.4.2.2. JSON¶

JSON (англ. JavaScript Object Notation, 1999 г.) — текстовый формат обмена данными, основанный на JavaScript. Одно из преимуществ — JSON легко читается людьми (англ. human-readable) (Листинг 8.1.7).

Листинг 8.1.7 — Пример JSON-файла | скачать

{
	"ФИО": "Иванов Сергей Михайлович",
	"ЕГЭ": {
		"Математика": 90,
		"Физика": 70,
		"Информатика": 80
	},
	"Хобби": ["Рисование", "Плавание"],
	"Возраст": 25.5,
	"ДомЖивотные": null
}

JSON-текст представляет собой одну из двух структур:

  • набор пар ключ: значение (словарь в терминологии Python), где ключ — строка, значение — любой тип;

  • упорядоченный набор значений (список в терминологии Python).

Значением может являться:

  • строка (в кавычках);

  • число;

  • логическое значение (true/false);

  • null;

  • одна из структур.

Одним из преимуществ JSON является близкое соответствие Python по типам данных. Работа с JSON-форматом поддерживается стандартным пакетом json, предоставляющем следующие основные функции:

json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

Сериализует объект obj, возвращая строку в JSON-формате.

Некоторые из параметров:

Параметры
  • obj – сериализуемый объект;

  • ensure_ascii – если равен False, запись не-ASCII значений происходит в файл «как есть», без преобразования в Unicode;

  • indent – величина отступа для вложенных структур.

json.loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

Десериализует объект (в том числе файловый) s, возвращая структуру в Python.

При ошибке десериализации возбуждается исключение JSONDecodeError.

exception json.JSONDecodeError(msg, doc, pos, end=None)

Класс исключения, возбуждаемый при ошибке в работе некоторых функций пакета.

Пример работы с JSON-форматом приведен в Листинге 8.1.8.

Листинг 8.1.8 — Работа с JSON-форматом в Python | скачать

import json

filename = "08_01_08_data.json"

info = {
    "ФИО": "Иванов Сергей Михайлович",
    "ЕГЭ": {
        "Математика": 90,
        "Физика": 70,
        "Информатика": 80
    },
    "Хобби": ["Рисование", "Плавание"],
    "Возраст": 25.5,
    "ДомЖивотные": None
}

# Запись структуры в файл в JSON-формате
with open(filename, "w", encoding="utf-8") as fh:
    fh.write(json.dumps(info, ensure_ascii=False, indent=4))

# Пример содержимого файла:
# {
#     "Хобби": [
#         "Рисование",
#         "Плавание"
#     ],
#     "ЕГЭ": {
#         "Информатика": 80,
#         "Математика": 90,
#         "Физика": 70
#     },
#     "ФИО": "Иванов Сергей Михайлович",
#     "ДомЖивотные": null,
#     "Возраст": 25.5
# }

# Чтение из файла JSON-формата
info_2 = []
with open(filename, encoding="utf-8") as fh:
    info_2 = json.loads(fh.read())

print(info_2)
# {'ФИО': 'Иванов Сергей Михайлович', 'ЕГЭ': {'Информатика': 80, 'Математика': 90, 'Физика': 70},
#  'Хобби': ['Рисование', 'Плавание'], 'Возраст': 25.5, 'ДомЖивотные': None}

Примечание

Валидация и оформление JSON-файлов

При возникновении ошибки чтения файла JSON-формата рекомендуется проверить его синтаксис с использованием специальных онлайн-сервисов, например, https://jsonformatter.curiousconcept.com/, которые могут как указать место ошибки, так и оформить его по стандарту.


1

Sebesta, W.S Concepts of Programming languages. 10E; ISBN 978-0133943023.

2

Python — официальный сайт. URL: https://www.python.org/.

3

Python — FAQ. URL: https://docs.python.org/3/faq/programming.html.

4

Саммерфилд М. Программирование на Python 3. Подробное руководство. — М.: Символ-Плюс, 2009. — 608 с.: ISBN: 978-5-93286-161-5.

5

Лучано Рамальо. Python. К вершинам мастерства. — М.: ДМК Пресс , 2016. — 768 с.: ISBN: 978-5-97060-384-0, 978-1-491-94600-8.

6

Что такое папка. URL: http://beginpc.ru/windows/chto-takoe-folder.

7

The Volume Does Not Contain A Recognized File System – How To Fix. URL: https://www.powerdatarecovery.com/hard-drive-recovery/volume-not-contain-recognized-file-system.html.

8

Последовательный доступ. URL: https://ru.wikipedia.org/wiki/Последовательный_доступ.

If you try to parse invalid JSON or decode an empty string as JSON, you will encounter the JSONDecodeError: Expecting value: line 1 column 1 (char 0). This error can occur if you read an empty file using json.load, read an empty JSON or receive an empty response from an API call.

You can use a try-except code block to catch the error and then check the contents of the JSON string or file before retrying.

This tutorial will go through the error in detail and how to solve it with code examples.


Table of contents

  • JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  • Example #1: Incorrect use of json.loads()
    • Solution
  • Example #2: Empty JSON file
    • Solution
  • Example #3: Response Request
  • Summary

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

In Python, JSONDecodeError occurs when there is an issue with the formatting of the JSON data. This specific error tells us the JSON decoder has encountered an empty JSON.

Example #1: Incorrect use of json.loads()

Let’s look at an example where we have a JSON file with the following contents:

[
        {"margherita":7.99},
        {"pepperoni":9.99},
        {"four cheeses":10.99}
]

We want to read the data into a program using the json library. Let’s look at the code:

import json

json_path = 'pizza.json'

data = json.loads(json_path)

In the above code, we try to read the data in using json.loads(). Let’s run the code to see the result:

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The error occurs because json.loads() expects a JSON encoded string, not a filename. The string pizza.json is not a valid JSON encoded string.

Solution

We need to use json.load() instead of json.loads() to read a file. Let’s look at the revised code:

import json

json_path = 'pizza.json'

with open(json_path, 'r') as f:

    data = json.loads(f.read())

print(data)

In the above code, we use the open() function to create a file object that json.load() can read and return the decoded data object. The with statement is a context manager that ensures that the file is closed once the code is complete. Let’s run the code to see the result:

[{'margherita': 7.99}, {'pepperoni': 9.99}, {'four cheeses': 10.99}]

Example #2: Empty JSON file

Let’s look at an example where we have an empty file, which we will try to read in using json.loads(). The file is called particles.json. Since the JSON file is empty, the JSON decoder will throw the JSONDecodeError when it tries to read the file’s contents. Let’s look at the code:

import json

filename = 'particles.json'

with open(filename, 'r') as f:
    contents = json.loads(f.read())
    print(contents)
---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
Input In [1], in <cell line: 5>()
      3 filename = 'particles.json'
      5 with open(filename, 'r') as f:
----> 6     contents = json.loads(f.read())
      7     print(contents)

File ~/opt/anaconda3/lib/python3.8/json/__init__.py:357, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    352     del kw['encoding']
    354 if (cls is None and object_hook is None and
    355         parse_int is None and parse_float is None and
    356         parse_constant is None and object_pairs_hook is None and not kw):
--> 357     return _default_decoder.decode(s)
    358 if cls is None:
    359     cls = JSONDecoder

File ~/opt/anaconda3/lib/python3.8/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
    332 def decode(self, s, _w=WHITESPACE.match):
    333     """Return the Python representation of ``s`` (a ``str`` instance
    334     containing a JSON document).
    335 
    336     """
--> 337     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338     end = _w(s, end).end()
    339     if end != len(s):

File ~/opt/anaconda3/lib/python3.8/json/decoder.py:355, in JSONDecoder.raw_decode(self, s, idx)
    353     obj, end = self.scan_once(s, idx)
    354 except StopIteration as err:
--> 355     raise JSONDecodeError("Expecting value", s, err.value) from None
    356 return obj, end

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Solution

If the file is empty, it is good practice to add a try-except statement to catch the JSONDecodeError. Let’s look at the code:

import json

filename = 'particles.json'

with open(filename, 'r') as f:

   try:

       contents = json.loads(f.read())

       print(contents)

   except json.decoder.JSONDecodeError:

       print('File is empty')
File is empty

Now we have verified the file is empty, we can add some content to the file. We will add three particle names together with their masses.

[
        {"proton":938.3},
        {"neutron":939.6},
        {"electron":0.51}
]

Let’s try to read the JSON file into our program and print the contents to the console:

import json

filename = 'particles.json'

with open(filename, 'r') as f:

   try:

       contents = json.loads(f.read())

       print(contents)

   except json.decoder.JSONDecodeError:

       print('File is empty')
[{'proton': 938.3}, {'neutron': 939.6}, {'electron': 0.51}]

We successfully read the contents of the file into a list object.

Example #3: Response Request

Let’s look at an example where we want to parse a JSON response using the requests library. We will send a RESTful GET call to a server, and in return, we get a response in JSON format. The requests library has a built-in JSON decoder, response.json(), which provides the payload data in the JSON serialized format.

We may encounter a response that has an error status code or is not content-type application/json. We must check that the response status code is 200 (OK) before performing the JSON parse. Let’s look at the code to check the response has the 200 status code and has the valid content type. application/json.

import requests

from requests.exceptions import HTTPError

url = 'https://httpbin.org/get'

try:

    response = requests.get(url)

    status = response.status_code

    if (status != 204 and response.headers["content-type"].strip().startswith("application/json")):

        try:

            json_response = response.json()

            print(json_response)

        except ValueError:
            
            print('Bad Data from Server. Response content is not valid JSON')

    elif (status != 204):

        try:

            print(response.text)

        except ValueError:

            print('Bad Data From Server. Reponse content is not valid text')

except HTTPError as http_err:

    print(f'HTTP error occurred: {http_err}')

except Exception as err:

    print(f'Other error occurred: {err}')

In the above example, we use httpbin.org to execute a GET call. Let’s run the code to get the result:

{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-6265a5c1-3b57327c02057a3a39ffe86d'}, 'origin': '90.206.95.191', 'url': 'https://httpbin.org/get'}

Summary

Congratulations on reading to the end of this tutorial! The JSONDecodeError: Expecting value: line 1 column 1 (char 0) occurs either when data is not present in a file, the JSON string is empty, or the payload from a RESTful call is empty.

You can resolve this error by checking the JSON file or string for valid content and using an if-statement when making a RESTful call to ensure the status code is 200 and the content type is application/json.

For further reading on errors involving JSON, go to the articles:

  • How to Solve Python ValueError: Trailing data
  • How to Solve Python JSONDecodeError: extra data
  • How to Solve Python TypeError: Object of type datetime is not JSON serializable
  • How to Solve Python JSONDecodeError: Expecting ‘,’ delimiter: line 1

Have fun and happy researching!

Сразу после появления, JSON быстро стал де факто стандартом обмена информации. Вероятно вы здесь из-за того, что вы хотите переместить данные из одного места в другое. Возможно вы получаете данные через API, или храните их в документной базе данных. Так или иначе, вы заинтересовались JSON, и вам нужно пользоваться им через Python.

Содержание

  • Подробнее про JSON
  • Структура JSON
  • Python поддерживает JSON
  • Небольшой словарь
  • Сериализация JSON
  • Пример сериализации JSON Python
  • Несколько полезных аргументов
  • Десериализация JSON
  • Пример десериализации JSON Python
  • Пример работы с JSON Python
  • Кодирование и декодирование объектов Python
  • Упрощение структур данных
  • Кодирование пользовательских типов
  • Декодирование пользовательских типов
  • Готово!

К счастью, это достаточно тривиальная задача, и как и с большинством тривиальных задач, Python делает все до омерзения простым.

Итак, используем ли мы JSON для хранения и обмена данными? Именно так. Это не более, чем стандартизированный формат, который используется сообществом для передачи данных. Помните, что JSON не является единственным доступным форматом для такой работы, XML и YAML наверное, единственные альтернативные способы, которые стоит упомянуть.

Не удивительно, что JavaScript Object Notation был вдохновен подмножеством языка программирования JavaScript, связанным с синтаксисом объектного литерала. У них есть отличный сайт, в котором все прекрасно объясняется. Не переживайте: JSON уже давно стал агностиком языка и существует как отдельный стандарт, по этому мы можем убрать JavaScript из этой дискуссии.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

В конечном счете, большая часть сообщества приняла JSON благодаря его простоте как для людей, так и для машин.
Смотрите, это JSON!

Структура JSON

Готовьтесь. Я собираюсь показать реальный пример JSON— такой же, какой вы встретите в реальной жизни. Это нормально, подразумевается что JSON является читаемым для любого, кто пользовался С-языками, а Python – это С-язык, так что мы говорим о вас!

{

    «firstName»: «Jane»,

    «lastName»: «Doe»,

    «hobbies»: [«running», «sky diving», «singing»],

    «age»: 35,

    «children»: [

        {

            «firstName»: «Alice»,

            «age»: 6

        },

        {

            «firstName»: «Bob»,

            «age»: 8

        }

    ]

}

Как видите, JSON поддерживает примитивные типы, такие как строки python и числа, а также вложенные списки и объекты.

Погодите, это выглядит как словарь Python, верно? На данный момент это достаточно универсальная нотация объектов, и не думаю что UON может так же легко отскакивать от зубов. Кстати, предлагайте альтернативы в комментариях!

НУ что же, вы пережили первый контакт с диким JSON. Теперь вам нужно научиться приручать его!

Python поддерживает JSON

Python содержит встроенный модуль под названием json для кодирования и декодирования данных JSON.

Просто импортируйте модуль в начале вашего файла:

Небольшой словарь

Как правило, процесс кодирования JSON называется сериализация. Этот термин обозначает трансформацию данных в серию байтов (следовательно, серийных) для хранения или передачи по сети. Также вы, возможно, уже слышали о термине «маршалинг», но это уже совсем другая область.

Естественно, десериализация — является противоположным процессом декодирования данных, которые хранятся или направлены в стандарт JSON.

Звучит как много технических терминов. Определенно. Но в реальности, все, о чем мы сейчас говорим — это чтение и написание. Представьте это следующим образом: кодирование это запись данных на диск, в то время как декодирование — это чтение данных в памяти.

Сериализация JSON

Что происходит после того, как компьютер обрабатывает большие объемы информации? Ему нужно принять дамп данных. Соответственно, модуль json предоставляет метод dump() для записи данных в файлы. Также есть метод dumps() для записей в строку Python.

Простые объекты Python переводятся в JSON согласно с весьма интуитивной конверсией.

Python JSON
dict object
list, tuple array
str string
int, long, float number
True true
False false
None null

Пример сериализации JSON Python

Представьте, что вы работаете с объектом Python в памяти, который выглядит следующим образом:

data = {

    «president»: {

        «name»: «Zaphod Beeblebrox»,

        «species»: «Betelgeusian»

    }

}

Сохранить эту информацию на диск — критично, так что ваша задача — записать на файл.

Используя контекстный менеджер Python, вы можете создать файл под названием data_file.json и открыть его в режиме write (файлы JSON имеют расширение .json).

with open(«data_file.json», «w») as write_file:

    json.dump(data, write_file)

Обратите внимание на то, что dump() принимает два позиционных аргумента: (1) объект данных, который сериализуется и (2), файловый объект, в который будут вписаны байты.

Или, если вы склонны продолжать использовать эти сериалзированные данные JSON в вашей программе, вы можете работать как со строкой.

json_string = json.dumps(data)

Обратите внимание, что файловый объект является пустым, так как вы на самом деле не выполняете запись на диск. Кроме того, dumps() аналогичен dump().

Ура! У вас получился малыш JSON и вы можете выпустить его в реальный мир, чтобы он вырос большим и сильным.

Несколько полезных аргументов

Помните, что JSON создан таким образом, чтобы быть читаемым для людей. Но читаемого синтаксиса недостаточно, если все слеплено вместе. Кроме этого, ваш стиль программирования скорее всего отличается от моего, и вам будет проще читать код, если он отформатирован по вашему вкусу.

Обратите внимание: Методы dump() и dumps() пользуются одними и теми же аргументами ключевых слов.

Первая опция, которую большинство людей хотят поменять, это пробел. Вы можете использовать аргумент indent для определения размера отступа вложенных структур. Ощутите разницу лично, используя данные, упомянутые выше и выполните следующие команды в консоли:

json.dumps(data)

json.dumps(data, indent=4)

Еще один вариант форматирования — это аргумент separators. По умолчанию, это двойной кортеж строк разделителя («, «, «: «), но обычно в качестве альтернативы для компактного JSON является («,», «:»). Взгляните на пример JSON еще раз, чтобы понять, где в игру вступают разделители.

Есть и другие аргументы, такие как sort_keys, но я не имею понятия, что он делает. Вы можете найти полный список в документации, если вам интересно.

Десериализация JSON

Отлично, похоже вам удалось поймать экземпляр дикого JSON! Теперь нам нужно предать ему форму. В модуле json вы найдете load() и loads() для превращения кодированных данных JSON в объекты Python.

Как и сериализация, есть простая таблица конверсии для десериализации, так что вы можете иметь представление о том, как все выглядит.

JSON Python
object dict
array list
string str
number (int) int
number (real) float
true True
false False
null None

Технически, эта конверсия не является идеальной инверсией таблицы сериализации. По сути, это значит что если вы кодируете объект сейчас, а затем декодируете его в будущем, вы можете не получить тот же объект назад. Я представляю это как своего рода телепортацию: мои молекулы распадаются в точке А и собираются в точке Б. Буду ли я тем же самым человеком?

В реальности, это как попросить одного друга перевести что-нибудь на японский, а потом попросить другого друга перевести это обратно на русский. В любом случае, самым простым примером будет кодировать кортеж и получить назад список после декодирования, вот так:

blackjack_hand = (8, «Q»)

encoded_hand = json.dumps(blackjack_hand)

decoded_hand = json.loads(encoded_hand)

print(blackjack_hand == decoded_hand) # False

print(type(blackjack_hand)) # <class ‘tuple’>

print(type(decoded_hand)) # <class ‘list’>

print(blackjack_hand == tuple(decoded_hand)) # True

Пример десериализации JSON Python

На этот раз, представьте что у вас есть некие данные, хранящиеся на диске, которыми вы хотите манипулировать в памяти. Вам все еще нужно будет воспользоваться контекстным менеджером, но на этот раз, вам нужно будет открыть существующий data_file.json в режиме для чтения.

with open(«data_file.json», «r») as read_file:

    data = json.load(read_file)

Здесь все достаточно прямолинейно, но помните, что результат этого метода может вернуть любые доступные типы данных из таблицы конверсий. Это важно только в том случае, если вы загружаете данные, которые вы ранее не видели. В большинстве случаев, корневым объектом будет dict или list.

Если вы внесли данные JSON из другой программы, или полученную каким-либо другим способом строку JSON форматированных данных в Python, вы можете легко десериализировать это при помощи loads(), который естественно загружается из строки:

json_string = «»»

{

    «researcher»: {

        «name»: «Ford Prefect»,

        «species»: «Betelgeusian»,

        «relatives»: [

            {

                «name»: «Zaphod Beeblebrox»,

                «species»: «Betelgeusian»

            }

        ]

    }

}

«»»

data = json.loads(json_string)

Ву а ля! Вам удалось укротить дикого JSON, теперь он под вашим контролем. Но что вы будете делать с этой силой — остается за вами. Вы можете кормить его, выращивать, и даже научить новым приемам. Не то чтобы я не доверяю вам, но держите его на привязи, хорошо?

Пример работы с JSON Python

Для тестового API, мы воспользуемся JSONPlaceholder, отличный источник фейковых данных JSON для практических целей.

Для начала, создайте файл под названием scratch.py, или как вам удобно. Здесь я не могу вас контролировать.

Вам нужно выполнить запрос API в сервис JSONPlaceholder, так что просто используйте пакет requests, чтобы он сделал за вас всю грязную работу. Добавьте следующие импорты вверху файла:

import json

import requests

Теперь вам предстоит поработать со списком TODOs, потому что… это своего рода обряд посвящения, вроде того.

Идем дальше и создаем запрос в API JSONPlaceholder для конечной точки GET /todos. Если вы слабо знакомы с запросами, есть очень удобный метод json(), который выполнит за вас всю работу, но вы можете попрактиковаться в использовании модуля json для десериализации атрибута текста объекта response. Это должно выглядеть следующим образом:

response = requests.get(«https://jsonplaceholder.typicode.com/todos»)

todos = json.loads(response.text)

Не верится, что это работает? Хорошо, запустите файл в интерактивном режиме и проверьте лично. Пока вы там, проверьте тип todos. Если вам любопытно, обратите внимание на первые 10 элементов в списке.

response = requests.get(«https://jsonplaceholder.typicode.com/todos»)

todos = json.loads(response.text)

print(todos == response.json()) # True

print(type(todos)) # <class ‘list’>

print(todos[:10]) # …

Как видите, никто вас не обманывает, и мы ценим здравый скептицизм.

Что такое интерактивный режим? Я уже думал вы не спросите! Вы знакомы с тем, что приходится постоянно прыгать туда-сюда между вашим редактором и терминалом? Мы, хитрые питонисты, используем интерактивный флаг -i, когда запускаем скрипт. Это отличный небольшой трюк для тестирования кода, так как он запускает скрипт, и затем открывает интерактивную командную строку с доступом ко всем данным скрипта!

Хорошо, теперь перейдем к действиям. Вы можете видеть структуру данных полученную от тестового API, посетив адрес в браузере https://jsonplaceholder.typicode.com/todos:

{

    «userId»: 1,

    «id»: 1,

    «title»: «delectus aut autem»,

    «completed»: false

}

Здесь несколько пользователей, каждый из которых имеет уникальный userId, а каждая задача имеет свойство Boolean. Можете определить, какие пользователи выполнили большую часть задач?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

response = requests.get(«https://jsonplaceholder.typicode.com/todos»)

todos = json.loads(response.text)

# Соотношение userId с числом выполненных пользователем задач.

todos_by_user = {}

# Увеличение выполненных задач каждым пользователем.

for todo in todos:

    if todo[«completed»]:

        try:

            # Увеличение количества существующих пользователей.

            todos_by_user[todo[«userId»]] += 1

        except KeyError:

            # Новый пользователь, ставим кол-во 1.

            todos_by_user[todo[«userId»]] = 1

# Создание отсортированного списка пар (userId, num_complete).

top_users = sorted(todos_by_user.items(),

                   key=lambda x: x[1], reverse=True)

#Получение максимального количества выполненных задач.

max_complete = top_users[0][1]

# Создание списка всех пользователей, которые выполнили

# максимальное количество задач.

users = []

for user, num_complete in top_users:

    if num_complete < max_complete:

        break

    users.append(str(user))

max_users = » and «.join(users)

Да, да, ваша версия лучше, но суть в том, что теперь вы можете манипулировать данными JSON как нормальным объектом Python!

Не знаю как вы, но я запустил скрипт в интерактивном режиме еще раз, и получил следующий результат:

s = «s» if len(users) > 1 else «»

print(f«user{s} {max_users} completed {max_complete} TODOs»)

# users 5 and 10 completed 12 TODOs

Это круто, и все такое, но мы занимаемся изучением JSON. В качестве вашей последней задачи, вы создадите файл JSON, который содержит готовые задачи (TODO) для каждого пользователя, который выполнил максимальное количество задач. Здесь мы использовали F-Строки Python.

Все, что вам нужно сделать, это отфильтровать задачи и вписать итоговый список в файл. Ради оригинальности, вы можете назвать файл выдачи filtered_data_file.json. Существует много способов сделать это, вот один из них:

# Определить функцию для фильтра выполненных задач

# с пользователями с максимально выполненными задачами.

def keep(todo):

    is_complete = todo[«completed»]

    has_max_count = todo[«userId»] in users

    return is_complete and has_max_count

# Записать отфильтрованные задачи в файл

with open(«filtered_data_file.json», «w») as data_file:

    filtered_todos = list(filter(keep, todos))

    json.dump(filtered_todos, data_file, indent=2)

Отлично, теперь вы избавились от всех данных, которые вам не нужны и сохранили необходимое для нового файла!

Запустите скрипт еще раз и проверьте filtered_data_file.json, чтобы убедиться в том, что все работает. Он будет в той же папке, что и scratch.py, когда вы запустите его.

Теперь, когда вы зашли так далеко, вы начинаете понимать что коснулись темы с большим потенциалом, не так ли? Не зазнавайтесь: скромность сестра таланта. Хотя и не могу не согласиться. Пока что мы работали в плавном потоке, но под конец мы можем поддать газку.

Кодирование и декодирование объектов Python

Что случается, когда мы пытаемся сериализировать класс Elf из приложения Dungeons & Dragons, с которым вы работаете?

class Elf:

    def __init__(self, level, ability_scores=None):

        self.level = level

        self.ability_scores = {

            «str»: 11, «dex»: 12, «con»: 10,

            «int»: 16, «wis»: 14, «cha»: 13

        } if ability_scores is None else ability_scores

        self.hp = 10 + self.ability_scores[«con»]

Ничего удивительного, Возникла ошибка, что класс Elf нельзя сериализировать:

elf = Elf(level=4)

json.dumps(elf)

TypeError: Object of type ‘Elf’ is not JSON serializable

Хотя, модуль json может обрабатывать большинство типов Python, он не понимает, как кодировать пользовательские типы данных по умолчанию. Это как пытаться поместить кубик в круглое отверстие — вам понадобится бензопила и надзор специалиста.

Упрощение структур данных

Сейчас вопрос в том, что делать с более сложными структурами данных. Например, вы можете попробовать кодировать и декодировать JSON вручную, но есть более разумное решение, которое избавит вас от лишней работы. Вместо того, чтобы идти непосредственно от пользовательского типа данных к JSON, вы можете перейти к промежуточному шагу.

Все что вам нужно, это отобразить ваши данные в контексте встроенных типов, которые изначально понятны json. По сути, вы переводите более сложный объект в его упрощенное представление, которое модуль json затем переводит в JSON.

Это наглядно представляется в математике: А = В, и В = С, значит А = С.

Чтобы добиться этого, вам нужен сложный объект для работы. Вы можете использовать любой пользовательский класс на ваш вкус, но Python имеет встроенный тип под названием complex, для представления сложных чисел, и он не может быть сериализированным по умолчанию. Итак, ради этих примеров, ваш сложный объект станет сложным объектом. Уже запутались?

z = 3 + 8j

print(type(z)) # <class ‘complex’>

json.dumps(z)

TypeError: Object of type ‘complex’ is not JSON serializable

Откуда приходят комплексные числа? Когда реальное число и представляемое число очень любят друг друга, они складываются вместе для создания числа, которое (справедливо) называется комплексным.

Хороший вопрос, который стоит задать себе при работе со сложными типами: «Какой минимальный объем информации необходим для воссоздания этого объекта?» В случае со комплексными числами, вам нужно знать только реальное и представляемое число, доступ к которым (в качестве атрибутов) доступен в объекте complex:

z = 3 + 8j

print(z.real) # 3.0

print(z.imag) # 8.0

Передачи одних и тех же чисел в сложный конструктор достаточно для удовлетворения оператора сравнения __eq__:

z = 3 + 8j

print( complex(3, 8) == z ) # True

Разбивать пользовательские типы данных на их составляющие компоненты — критически важно как для процесса сериализации, так и для десериализации.

Кодирование пользовательских типов

Для перевода пользовательского объекта в JSON, все что вам нужно — это предоставить функцию кодирования параметру по умолчанию метода dump(). Модуль json вызовет эту функцию для любых объектов, которые не являются естественно сериализируемыми. Вот простая функция декодирования, которую вы можете использовать на практике:

def encode_complex(z):

    if isinstance(z, complex):

        return (z.real, z.imag)

    else:

        type_name = z.__class__.__name__

        raise TypeError(f«Object of type ‘{type_name}’ is not JSON serializable»)

Обратите внимание на то, что ожидается получение ошибки TypeError, если вы не получите ожидаемый тип объекта. Таким образом, вы избегаете случайной сериализации любых пользовательских типов. Теперь вы можете кодировать сложные объекты самостоятельно!

>>> json.dumps(9 + 5j, default=encode_complex)

‘[9.0, 5.0]’

>>> json.dumps(elf, default=encode_complex)

TypeError: Object of type ‘Elf’ is not JSON serializable

Почему мы кодируем комплексное число как кортеж? Хороший вопрос. Это определенно не является единственными выбором, впрочем, как и не является лучшим выбором. Фактически, это не может быть хорошим отображением, если вы планируете декодировать объект в будущем, что вы и увидите дальше.

Еще один частый подход — создать дочерний класс JSONEncoder и переопределить его метод default():

class ComplexEncoder(json.JSONEncoder):

    def default(self, z):

        if isinstance(z, complex):

            return (z.real, z.imag)

        else:

            super().default(self, z)

Вместо создания ошибки TypeError, вы можете дать классу base справиться с ней. Вы можете использовать его как напрямую в метод dump() при помощи параметра cls, или создав экземпляр encoder-а и вызова метода encode():

>>> json.dumps(2 + 5j, cls=ComplexEncoder)

‘[2.0, 5.0]’

>>> encoder = ComplexEncoder()

>>> encoder.encode(3 + 6j)

‘[3.0, 6.0]’

Декодирование пользовательских типов

В то время, как реальные и представляемые части сложных чисел абсолютно необходимы, на самом деле их не совсем достаточно для воссоздания объекта. Вот что случается, если вы попробуете кодировать комплексное число при помощи ComplexEncoder, а затем декодировать результат:

>>> complex_json = json.dumps(4 + 17j, cls=ComplexEncoder)

>>> json.loads(complex_json)

[4.0, 17.0]

Все что вы получаете обратно — это список, и вы можете передать значения в конструктор, если вы хотите получить этот сложный объект еще раз. Напоминаю о нашем разговоре о телепортации. Чего нам в итоге не хватает? Метаданные, или информации о типа данных, которые вы кодируете.

Я предполагаю, что вопрос, который действительно важно задать себе, это «Какое минимальное количество информации, которая необходима, и которой достаточно для воссоздания объекта?»

Модуль json ожидает, что все пользовательские типы будут отображаться как объекты стандарта JSON. Для разнообразия, вы можете создать файл JSON, на этот раз назовем его complex_data.json и добавим следующий объект, отображающий комплексное число:

{

    «__complex__»: true,

    «real»: 42,

    «imag»: 36

}

Заметили хитрую часть? Вот ключ «__complex__» является метаданными, которые мы только что упоминали. Неважно, какое ассоциируемое значение мы имеем. Чтобы эта хитрость сработала, все что вам нужно, это подтвердить существование ключа:

def decode_complex(dct):

    if «__complex__» in dct:

        return complex(dct[«real»], dct[«imag»])

    return dct

Если «__complex__» не находится в словаре, вы можете просто вернуть объект и позволить декодеру по умолчанию разобраться с этим.

Каждый раз, когда метод load() пытается парсить объект, у вас есть возможность выступить в роли посредника, перед тем как декодер пройдет свой путь с данными. Вы можете сделать это, спарсив вашу функцию декодирования с параметром object_hook.

Теперь сыграем в ту же игру, что и раньше:

with open(«complex_data.json») as complex_data:

    data = complex_data.read()

    z = json.loads(data, object_hook=decode_complex)

print(type(z)) # <class ‘complex’>

Хотя объект object_hook может показаться аналогом параметра по умолчанию метода dump(), на самом деле аналогия здесь же и заканчивается.

Это не просто работает с одним объектом. Попробуйте внести этот список сложных чисел в complex_data.json и запустить скрипт еще раз:

[

  {

    «__complex__»:true,

    «real»:42,

    «imag»:36

  },

  {

    «__complex__»:true,

    «real»:64,

    «imag»:11

  }

]

Если все идет гладко, вы получите список комплексных объектов:

with open(«complex_data.json») as complex_data:

    data = complex_data.read()

    numbers = json.loads(data, object_hook=decode_complex)

print(numbers) # [(42+36j), (64+11j)]

Вы также можете попробовать создать подкласс JSONDecoder и переопределить object_hook, но лучше придерживаться более легких решений при каждой возможности.

Итоги

Поздравляю, теперь вы обладаете могущественной силой JSON для любых ваших потребностей в Python.

Хотя примеры, с которыми вы работали, безусловно, оригинальные и чрезмерно упрощены, они демонстрируют рабочий процесс, который вы можете применить к более общим задачам:

  1. Импорт модуля json
  2. Чтение данных с load() или loads()
  3. Обработка данных
  4. Запись измененных данных при помощи dump() или dumps()

Что вы будете делать с данными, после того как они загрузились в память — зависит от вашего случая. В целом, ваша задача — собрать данные из источника, извлечение полезной информации, и передача этой информации (или ее запись).

Сегодня вы проделали небольшое путешествие: вы поймали и приручили JSON, и вернулись домой как раз к ужину! И в качестве бонуса, научившись работать с модулем json, можете начать изучение модулей pickle и marshal.

Спасибо за внимание, и удачи с вашими начинаниями в Python!

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

E-mail: vasile.buldumac@ati.utm.md

Образование
Universitatea Tehnică a Moldovei (utm.md)

  • 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»

{
[
{
«id»:1,
«Рус. Название»:»Храм Василия Блаженного»,
«Англ. Название»:»St. Basil’s Cathedral»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Храм_Василия_Блаженного»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Saint_Basil%27s_Cathedral»,
«Адрес»:»Красная площадь, 7″,
«Координаты»:»55.75253278804399,37.62309622086314″
},
{
«id»:2,
«Рус. Название»:»Памятник Минину и Пожарскому»,
«Англ. Название»:»Monument to Minin and Pozharsky»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Памятник_Минину_и_По…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Monument_to_Minin_an…»,
«Адрес»:»Красная площадь, 7″,
«Координаты»:»55.75278691817848,37.622645609748645″
},
{
«id»:3,
«Рус. Название»:»Спасская башня»,
«Англ. Название»:»Spasskaya Tower»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Спасская_башня»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Spasskaya_Tower»,
«Адрес»:»Тверской район, Кремль, 12″,
«Координаты»:»55.75263262508084,37.62150298870831″
},
{
«id»:4,
«Рус. Название»:»Лобное место на Красной Площади»,
«Англ. Название»:»Lobnoye mesto»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Лобное_место»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Lobnoye_Mesto»,
«Адрес»:»Красная площадь»,
«Координаты»:»55.753216515339,37.62251149929791″
},
{
«id»:5,
«Рус. Название»:»Мавзолей Ленина»,
«Англ. Название»:»Lenin’s Mausoleum»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Мавзолей_Ленина»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Lenin%27s_Mausoleum»,
«Адрес»:»Красная площадь»,
«Координаты»:»55.7537349753831,37.61993877843665″
},
{
«id»:6,
«Рус. Название»:»ГУМ»,
«Англ. Название»:»GUM»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/ГУМ»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/GUM_(department_store)»,
«Адрес»:»Красная площадь, 3″,
«Координаты»:»55.75452047240065,37.621000934165615″
},
{
«id»:8,
«Рус. Название»:»Столовая № 57 «,
«Англ. Название»:»Dining room No. 57″,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»»,
«Англ. Wiki»:»»,
«Адрес»:»Красная площадь, 3″,
«Координаты»:»55.754176181320865,37.623260171748136″
},
{
«id»:9,
«Рус. Название»:»Казанский собор»,
«Англ. Название»:»Kazan Cathedral»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Казанский_собор_(Москва)»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Kazan_Cathedral,_Moscow»,
«Адрес»:»Никольская улица, 3″,
«Координаты»:»55.75539606348181,37.61918185441502″
},
{
«id»:10,
«Рус. Название»:»Исторический музей «,
«Англ. Название»:»Historical Museum»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Государственный_исто…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/State_Historical_Museum»,
«Адрес»:»Красная площадь, 1″,
«Координаты»:»55.755393354751924,37.61799095361239″
},
{
«id»:11,
«Рус. Название»:»Воскресенские ворота»,
«Англ. Название»:»Resurrection Gate»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Воскресенские_ворота»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Iberian_Gate_and_Chapel»,
«Адрес»:»проезд Воскресенские Ворота, 1А»,
«Координаты»:»55.75566505507187,37.618045211312165″
},
{
«id»:12,
«Рус. Название»:»Нулевой километр»,
«Англ. Название»:»Kilometre Zero»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»»,
«Англ. Wiki»:»»,
«Адрес»:»проезд Воскресенские Ворота»,
«Координаты»:»55.75582236123602,37.61769652414025″
},
{
«id»:13,
«Рус. Название»:»Александровский сад «,
«Англ. Название»:»Alexander Garden»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Александровский_сад_…»,
«Англ. Wiki»:»https://en.wikipedia.org/wiki/Alexander_Garden»,
«Адрес»:»Александровский сад»,
«Координаты»:»55.75211610144802,37.613610828416256″
},
{
«id»:15,
«Рус. Название»:»Охотный ряд «,
«Англ. Название»:»Okhotny Ryad»,
«Категория»:»Объект Красной Площади»,
«Рус. Wiki»:»https://ru.wikipedia.org/wiki/Охотный_Ряд»,
«Англ. Wiki»:»»,
«Адрес»:»пл. Манежная, д. 1, стр. 2″,
«Координаты»:»55.755758,37.61468″
}
]
}

If you try to parse invalid JSON or decode an empty string as JSON, you will encounter the JSONDecodeError: Expecting value: line 1 column 1 (char 0). This error can occur if you read an empty file using json.load, read an empty JSON or receive an empty response from an API call.

You can use a try-except code block to catch the error and then check the contents of the JSON string or file before retrying.

This tutorial will go through the error in detail and how to solve it with code examples.


Table of contents

  • JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  • Example #1: Incorrect use of json.loads()
    • Solution
  • Example #2: Empty JSON file
    • Solution
  • Example #3: Response Request
  • Summary

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

In Python, JSONDecodeError occurs when there is an issue with the formatting of the JSON data. This specific error tells us the JSON decoder has encountered an empty JSON.

Example #1: Incorrect use of json.loads()

Let’s look at an example where we have a JSON file with the following contents:

[
        {"margherita":7.99},
        {"pepperoni":9.99},
        {"four cheeses":10.99}
]

We want to read the data into a program using the json library. Let’s look at the code:

import json

json_path = 'pizza.json'

data = json.loads(json_path)

In the above code, we try to read the data in using json.loads(). Let’s run the code to see the result:

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The error occurs because json.loads() expects a JSON encoded string, not a filename. The string pizza.json is not a valid JSON encoded string.

Solution

We need to use json.load() instead of json.loads() to read a file. Let’s look at the revised code:

import json

json_path = 'pizza.json'

with open(json_path, 'r') as f:

    data = json.loads(f.read())

print(data)

In the above code, we use the open() function to create a file object that json.load() can read and return the decoded data object. The with statement is a context manager that ensures that the file is closed once the code is complete. Let’s run the code to see the result:

[{'margherita': 7.99}, {'pepperoni': 9.99}, {'four cheeses': 10.99}]

Example #2: Empty JSON file

Let’s look at an example where we have an empty file, which we will try to read in using json.loads(). The file is called particles.json. Since the JSON file is empty, the JSON decoder will throw the JSONDecodeError when it tries to read the file’s contents. Let’s look at the code:

import json

filename = 'particles.json'

with open(filename, 'r') as f:
    contents = json.loads(f.read())
    print(contents)
---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
Input In [1], in <cell line: 5>()
      3 filename = 'particles.json'
      5 with open(filename, 'r') as f:
----> 6     contents = json.loads(f.read())
      7     print(contents)

File ~/opt/anaconda3/lib/python3.8/json/__init__.py:357, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    352     del kw['encoding']
    354 if (cls is None and object_hook is None and
    355         parse_int is None and parse_float is None and
    356         parse_constant is None and object_pairs_hook is None and not kw):
--> 357     return _default_decoder.decode(s)
    358 if cls is None:
    359     cls = JSONDecoder

File ~/opt/anaconda3/lib/python3.8/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
    332 def decode(self, s, _w=WHITESPACE.match):
    333     """Return the Python representation of ``s`` (a ``str`` instance
    334     containing a JSON document).
    335 
    336     """
--> 337     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338     end = _w(s, end).end()
    339     if end != len(s):

File ~/opt/anaconda3/lib/python3.8/json/decoder.py:355, in JSONDecoder.raw_decode(self, s, idx)
    353     obj, end = self.scan_once(s, idx)
    354 except StopIteration as err:
--> 355     raise JSONDecodeError("Expecting value", s, err.value) from None
    356 return obj, end

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Solution

If the file is empty, it is good practice to add a try-except statement to catch the JSONDecodeError. Let’s look at the code:

import json

filename = 'particles.json'

with open(filename, 'r') as f:

   try:

       contents = json.loads(f.read())

       print(contents)

   except json.decoder.JSONDecodeError:

       print('File is empty')
File is empty

Now we have verified the file is empty, we can add some content to the file. We will add three particle names together with their masses.

[
        {"proton":938.3},
        {"neutron":939.6},
        {"electron":0.51}
]

Let’s try to read the JSON file into our program and print the contents to the console:

import json

filename = 'particles.json'

with open(filename, 'r') as f:

   try:

       contents = json.loads(f.read())

       print(contents)

   except json.decoder.JSONDecodeError:

       print('File is empty')
[{'proton': 938.3}, {'neutron': 939.6}, {'electron': 0.51}]

We successfully read the contents of the file into a list object.

Example #3: Response Request

Let’s look at an example where we want to parse a JSON response using the requests library. We will send a RESTful GET call to a server, and in return, we get a response in JSON format. The requests library has a built-in JSON decoder, response.json(), which provides the payload data in the JSON serialized format.

We may encounter a response that has an error status code or is not content-type application/json. We must check that the response status code is 200 (OK) before performing the JSON parse. Let’s look at the code to check the response has the 200 status code and has the valid content type. application/json.

import requests

from requests.exceptions import HTTPError

url = 'https://httpbin.org/get'

try:

    response = requests.get(url)

    status = response.status_code

    if (status != 204 and response.headers["content-type"].strip().startswith("application/json")):

        try:

            json_response = response.json()

            print(json_response)

        except ValueError:
            
            print('Bad Data from Server. Response content is not valid JSON')

    elif (status != 204):

        try:

            print(response.text)

        except ValueError:

            print('Bad Data From Server. Reponse content is not valid text')

except HTTPError as http_err:

    print(f'HTTP error occurred: {http_err}')

except Exception as err:

    print(f'Other error occurred: {err}')

In the above example, we use httpbin.org to execute a GET call. Let’s run the code to get the result:

{'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.27.1', 'X-Amzn-Trace-Id': 'Root=1-6265a5c1-3b57327c02057a3a39ffe86d'}, 'origin': '90.206.95.191', 'url': 'https://httpbin.org/get'}

Summary

Congratulations on reading to the end of this tutorial! The JSONDecodeError: Expecting value: line 1 column 1 (char 0) occurs either when data is not present in a file, the JSON string is empty, or the payload from a RESTful call is empty.

You can resolve this error by checking the JSON file or string for valid content and using an if-statement when making a RESTful call to ensure the status code is 200 and the content type is application/json.

For further reading on errors involving JSON, go to the articles:

  • How to Solve Python ValueError: Trailing data
  • How to Solve Python JSONDecodeError: extra data
  • How to Solve Python TypeError: Object of type datetime is not JSON serializable
  • How to Solve Python JSONDecodeError: Expecting ‘,’ delimiter: line 1

Have fun and happy researching!

Many developers store data from a program in a JSON file; other programs reference APIs which require working with JSON. Indeed, you will have no trouble finding a use case for JSON, or its Python equivalent, dictionaries.

You may encounter a JSONDecodeError when you are working with JSON data. In this guide, we’re going to talk about the causes of a JSONDecodeError and how to fix this error.

Get offers and scholarships from top coding schools illustration

Find Your Bootcamp Match

  • Career Karma matches you with top tech bootcamps
  • Access exclusive scholarships and prep courses

Select your interest

First name

Last name

Email

Phone number

By continuing you agree to our Terms of Service and Privacy Policy, and you consent to receive offers and opportunities from Career Karma by telephone, text message, and email.

Python JSONDecodeError

A Python JSONDecodeError indicates there is an issue with the way in which your JSON data is formatted. For example, your JSON data may be missing a curly bracket, or have a key that does not have a value, or be missing some other piece of syntax.

To completely fix a JSONDecodeError, you need to go into a JSON file to see what the problem is. If you anticipate multiple problems coming up in the future, you may want to use a try…except block to handle your JSONDecodeError.

Followed by the JSONDecodeError keyword, you should see a short description which describes the cause of the error.

All properly-formatted JSON should look like this:

“value” can be any valid JSON value, such as a list, a string, or another JSON object.

An Example Scenario

We are building a program that stores a list of JSON objects which represent which computers have been issued to employees at a business. Each JSON object should look like this:

[
	{
		"name": "Employee Name",
		"equip_id": "000"
	}
]

We store these JSON objects in a file called equipment.json. The file only contains one entry:

[
	{
		"name": "Laura Harper",
		"equip_id" "309"
	}
]

To read this data into our program, we can use the json module:

import json

with open("equipment.json") as file:
	data = json.load(file)

print("Equipment data has been successfully retrieved.")

First, we import the json module which we use to read a JSON file. Then, we use an open() statement to read the contents of our JSON file. We print out a message to the console that tells us the equipment data has been retrieved once our with statement has run.

Let’s run our code and see what happens:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "/usr/lib/python3.8/json/__init__.py", line 293, in load
	return loads(fp.read(),
  File "/usr/lib/python3.8/json/__init__.py", line 357, in loads
	return _default_decoder.decode(s)
  File "/usr/lib/python3.8/json/decoder.py", line 337, in decode
	obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.8/json/decoder.py", line 353, in raw_decode
	obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting ':' delimiter: line 4 column 16 (char 47

Our code returns a long error. We can see Python describes the cause of our error after the term JSONDecodeError.

The Solution

Our JSONDecodeError is telling us we are missing a colon (:) in our JSON data. This colon should appear on line four at column 16. If we look at this line of data in our equipment.json file, we can see our JSON is invalid:

Our code is missing a colon. To fix this error, we should add a colon:

Now that we have fixed the problem with the way in which our data is represented, we can try to run our program again:

Equipment data has been successfully retrieved.

Our code executes successfully.

Alternatively, we could use a try…except handler to handle this issue so that our code will not immediately return an error if we face another formatting issue:

import json

try:
	with open("equipment.json") as file:
		data = json.load(file)

	print("Equipment data has been successfully retrieved.")
except json.decoder.JSONDecodeError:
	print("There was a problem accessing the equipment data.")

If there is an error in our JSON data, this program will return:

There was a problem accessing the equipment data.

Otherwise, the program will read the data and then display the following text on the console:

Equipment data has been successfully retrieved.

Conclusion

The Python JSONDecodeError indicates there is an issue with how a JSON object is formatted. To fix this error, you should read the error message and use it to guide you in fixing your JSON data. Alternatively, you can use a try…except block to catch and handle the error.

Are you interested in learning more about Python coding? Read our How to Learn Python guide. You’ll find expert advice on how to learn Python and a list of learning resources to help you build your knowledge.

  • Ошибка деретикс call of duty modern warfare
  • Ошибка делить на ноль excel
  • Ошибка делим на слоги
  • Ошибка деления на ноль sql server
  • Ошибка деления на ноль java