I want to save three dictionaries alternately in a json file as an array of objects.

 import json milk = {'Product': 'Milk', 'Calories': 100,} bread = {' Product ':' Bread ',' Calories': 200,} fish = {'Product': 'Fish', ' Calories: 300,} def add_food_to_database (food): path = '/home/dzmitry/foods.json' with open (path, 'r +') as jsonfile: if not jsonfile.read (): data = [] data. append (food) json.dump (data, jsonfile) else: jsonfile.seek (0) data = json.load (jsonfile) data.append (food) json.dump (data, jsonfile) print ("Added product:", food ['Product']) foods = (milk, bread, fish) [add_food_to_database (food) for food in foods] 

However, for some reason, only two products are added.

 =========== RESTART: /home/dzmitry/forstack.py ===========
 Product added: Milk
 Product Added: Bread
 Traceback (most recent call last):
   File "/home/dzmitry/forstack.py", line 28, in 
     [add_food_to_database (food) for food in foods]
   File "/home/dzmitry/forstack.py", line 28, in 
     [add_food_to_database (food) for food in foods]
   File "/home/dzmitry/forstack.py", line 22, in add_food_to_database
     data = json.load (jsonfile)
   File "/usr/lib/python3.5/json/__init__.py", line 268, in load
     parse_constant = parse_constant, object_pairs_hook = object_pairs_hook, ** kw)
   File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
     return _default_decoder.decode (s)
   File "/usr/lib/python3.5/json/decoder.py", line 342, in decode
     raise JSONDecodeError ("Extra data", s, end)
 json.decoder.JSONDecodeError: Extra data: line 1 column 140 (char 139)
 >>> 

What am I doing wrong?

  • one
    add to the question the contents of the file "foods.json" after the error - Igor
  • tested your code, that's what comes out in the JSON file [{"Product": "Mikl", "Calories": 100}][{"Product": "Milk", "Calories": 100}, {"Product": "Bread", "Calories": 200}] , that is, it adds everything, but the format of JSON'a you get is not correct. Should be displayed in the file as - [{"Product": "Milk", "Calories": 100}, {"Product": "Milk", "Calories": 100}, {"Product": "Bread", "Calories": 200}] - Insider
  • as shown by @Insider, you have json.dump(data, jsonfile) code json.dump(data, jsonfile) appends the file - in addition to the previous data. The file must be rediscovered to create or, at least, unwind to the beginning. - Igor
  • And in the end, in print, you only have Milk and Bread, because they are in the correct JSON format. - Insider

1 answer 1

json.load() reads from a file, so when you call json.dump() , it writes to the end - as a result, incorrect content is obtained. For example, if the file contained [1] list, and food = 2 , then after json.dump() contents would be [1][1,2] , which is not a JSON list.

Minimum change: add jsonfile.seek(0) before json.dump() (the recorded content is more, so it is not necessary to jsonfile.truncate(0) or similar to call to remove the previous content).

Instead of a JSON list, you can save individual objects to a file — one per line. Then, you can simplify the add_food_to_database() function:

 import json def add_food_to_database(food, path='foods.json'): with open(path, 'a', encoding='utf-8') as file: print(json.dumps(food, separators=(',', ':')), file=file) 

json.dumps() does not contain a new line (unshielded \n forbidden in JSON lines and json.dumps() does not use '\n' for default formatting — the separators passed explicitly in the example in order not to depend on the default value).

To read back:

 foods = [json.loads(line) for line in input_json_file] 

Or (if you want to work with the file as a database) you can use sqlite to save the records:

 import sqlite3 def add_food_to_database(food, path='foods.db'): with sqlite3.connect(path) as db: db.execute('insert into food values (:product, :calories)', food) 

The example assumes that food = {'product': 'Молоко', 'calories': 100} or similar and foods.db contains a table created with:

 db.execute('create table food (product, calories)') 
  • Thank you, it works! And with sqlite it is much more natural! - pynix