86

Hi I am trying to take the data from a json file and insert and id then perform POST REST. my file data.json has:

{
    'name':'myname'
}

and I would like to add an id so that the json data looks like:

 {
     'id': 134,
     'name': 'myname'
 }

So I tried:

import json
f = open("data.json","r")
data = f.read()
jsonObj = json.loads(data)

I can't get to load the json format file. What should I do so that I can convert the json file into json object and add another id value.

falsetru
  • 357,413
  • 63
  • 732
  • 636
codeBarer
  • 2,238
  • 7
  • 44
  • 75

7 Answers7

120

Set item using data['id'] = ....

import json

with open('data.json', 'r+') as f:
    data = json.load(f)
    data['id'] = 134 # <--- add `id` value.
    f.seek(0)        # <--- should reset file position to the beginning.
    json.dump(data, f, indent=4)
    f.truncate()     # remove remaining part
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 8
    unrelated: json format is defined for Unicode text. You could use `with codecs.open('data.json', 'r+', encoding='utf-8') as f` – jfs Jan 10 '14 at 04:51
  • 1
    @VladimBelov answer is better in my opinion. Though It doesn't need the `os.remove(filename)` because opening the file in 'w' (write) mode will truncate as necessary. – markm Jun 28 '18 at 02:09
  • If I am adding multiple `id`s such as `data['id_1'] = 123"` as well, should I do `f.seek(0); dump(); truncate()` for that line also or only doing single `f.seek(0) ; json.dump(data, jsonFile); f.truncate()` on the end for the all added `id`s would be enough? @falsetru – alper Oct 26 '18 at 13:15
  • Does `json.dump(data, jsonFile); f.truncate()` updates only the added section on the file or writes complete file all over again? – alper Oct 26 '18 at 13:21
  • 1
    @alper, You need only single `f.seek(0)` call after the last `d['...'] = ...`. `f.seek(0); json.dump(...); f.truncate()` write complete file all over again. – falsetru Oct 26 '18 at 13:47
  • @falsetru: Thanks. If a single `id` is updated/added, writing complete file seems little bit inefficient if there is thousands of lines. Does `json` allow us to only change the updated line instead of writing complete file all over again? – alper Oct 26 '18 at 14:03
  • 1
    @alper, (1) Inserting something in file requires rewrite after the position. (2) It's hard to know where the insertion position will be (depending on json serialization implementation.) – falsetru Oct 26 '18 at 14:39
  • @falsetru is `truncate()` required? `json.dump()` seems to automatically truncate the file. – Kalcifer Jan 02 '21 at 06:25
  • 1
    @Kalcifer, It is required. especially when new id's representation is shorter than old one. And, `json.dump()` does not call `truncate` according to [code](https://github.com/python/cpython/blob/11276cd9c49faea66ce7760f26a238d1edbf6421/Lib/json/__init__.py#L120-L180) – falsetru Jan 02 '21 at 10:04
  • changing the file in place is a nice thing. – Ulf Gjerdingen Oct 08 '21 at 08:47
51

falsetru's solution is nice, but has a little bug:

Suppose original 'id' length was larger than 5 characters. When we then dump with the new 'id' (134 with only 3 characters) the length of the string being written from position 0 in file is shorter than the original length. Extra chars (such as '}') left in file from the original content.

I solved that by replacing the original file.

import json
import os

filename = 'data.json'
with open(filename, 'r') as f:
    data = json.load(f)
    data['id'] = 134 # <--- add `id` value.

os.remove(filename)
with open(filename, 'w') as f:
    json.dump(data, f, indent=4)
VadimBelov
  • 671
  • 6
  • 3
11

I would like to present a modified version of Vadim's solution. It helps to deal with asynchronous requests to write/modify json file. I know it wasn't a part of the original question but might be helpful for others.

In case of asynchronous file modification os.remove(filename) will raise FileNotFoundError if requests emerge frequently. To overcome this problem you can create temporary file with modified content and then rename it simultaneously replacing old version. This solution works fine both for synchronous and asynchronous cases.

import os, json, uuid

filename = 'data.json'
with open(filename, 'r') as f:
    data = json.load(f)
    data['id'] = 134 # <--- add `id` value.
    # add, remove, modify content

# create randomly named temporary file to avoid 
# interference with other thread/asynchronous request
tempfile = os.path.join(os.path.dirname(filename), str(uuid.uuid4()))
with open(tempfile, 'w') as f:
    json.dump(data, f, indent=4)

# rename temporary file replacing old file
os.rename(tempfile, filename)
Vadym Pasko
  • 339
  • 4
  • 4
  • due to [this issue](https://stackoverflow.com/questions/8107352/force-overwrite-in-os-rename) it would be better to use `os.replace(tempfile, filename)` for a cross-platform solution – Vadym Pasko Feb 05 '19 at 14:02
3

There is really quite a number of ways to do this and all of the above are in one way or another valid approaches... Let me add a straightforward proposition. So assuming your current existing json file looks is this....

{
     "name":"myname"
}

And you want to bring in this new json content (adding key "id")

{
     "id": "134",
     "name": "myname"
 }

My approach has always been to keep the code extremely readable with easily traceable logic. So first, we read the entire existing json file into memory, assuming you are very well aware of your json's existing key(s).

import json 

# first, get the absolute path to json file
PATH_TO_JSON = 'data.json' #  assuming same directory (but you can work your magic here with os.)

# read existing json to memory. you do this to preserve whatever existing data. 
with open(PATH_TO_JSON,'r') as jsonfile:
    json_content = json.load(jsonfile) # this is now in memory! you can use it outside 'open'

Next, we use the 'with open()' syntax again, with the 'w' option. 'w' is a write mode which lets us edit and write new information to the file. Here s the catch that works for us ::: any existing json with the same target write name will be erased automatically.

So what we can do now, is simply write to the same filename with the new data

# add the id key-value pair (rmbr that it already has the "name" key value)
json_content["id"] = "134"

with open(PATH_TO_JSON,'w') as jsonfile:
    json.dump(json_content, jsonfile, indent=4) # you decide the indentation level

And there you go! data.json should be good to go for an good old POST request

aaronlhe
  • 1,062
  • 10
  • 18
3

This implementation should suffice:

with open(jsonfile, 'r') as file:
    data = json.load(file)
    data[id] = value

with open(jsonfile, 'w') as file:
    json.dump(data, file)

using context manager for the opening of the jsonfile. data holds the updated object and dumped into the overwritten jsonfile in 'w' mode.

Andrew Eckart
  • 1,618
  • 9
  • 15
abee44
  • 31
  • 1
2

try this script:

with open("data.json") as f:
    data = json.load(f)
    data["id"] = 134
    json.dump(data, open("data.json", "w"), indent = 4)

the result is:

{
    "name":"mynamme",
    "id":134
}

Just the arrangement is different, You can solve the problem by converting the "data" type to a list, then arranging it as you wish, then returning it and saving the file, like that:

index_add = 0
with open("data.json") as f:
    data = json.load(f)
    data_li = [[k, v] for k, v in data.items()]
    data_li.insert(index_add, ["id", 134])
    data = {data_li[i][0]:data_li[i][1] for i in range(0, len(data_li))}
    json.dump(data, open("data.json", "w"), indent = 4)

the result is:

{
    "id":134,
    "name":"myname"
}

you can add if condition in order not to repeat the key, just change it, like that:

index_add = 0
n_k = "id"
n_v = 134
with open("data.json") as f:
    data = json.load(f)
    if n_k in data:
        data[n_k] = n_v
    else:
       data_li = [[k, v] for k, v in data.items()]
       data_li.insert(index_add, [n_k, n_v])
       data = {data_li[i][0]:data_li[i][1] for i in range(0, len(data_li))}
    json.dump(data, open("data.json", "w"), indent = 4)
0

Not exactly your solution but might help some people solving this issue with keys. I have list of files in folder, and i need to make Jason out of it with keys. After many hours of trying the solution is simple.

Solution:

async def return_file_names():
        dir_list = os.listdir("./tmp/")
        json_dict = {"responseObj":[{"Key": dir_list.index(value),"Value": value} for value in dir_list]}
        print(json_dict)
        return(json_dict)

Response look like this:

{
  "responseObj": [
    {
      "Key": 0,
      "Value": "bottom_mask.GBS"
    },
    {
      "Key": 1,
      "Value": "bottom_copper.GBL"
    },
    {
      "Key": 2,
      "Value": "copper.GTL"
    },
    {
      "Key": 3,
      "Value": "soldermask.GTS"
    },
    {
      "Key": 4,
      "Value": "ncdrill.DRD"
    },
    {
      "Key": 5,
      "Value": "silkscreen.GTO"
    }
  ]
}
met_xx
  • 1
  • Welcome to Stack Overflow! Please read [How to Answer](https://stackoverflow.com/help/how-to-answer). Your answer doesn't relate to the question, which is about reading and modifying the contents of a json file. – craigb Oct 30 '22 at 21:30