Removing JSON element of array in Python with same file descriptor - python

I have a JSON file that has an array of objects like this:
{
"array": [
{
"foo1": "bar",
"spam1": "eggs"
},
{
"foo2": "bar",
"spam2": "eggs"
},
{
"foo3": "bar",
"spam3": "eggs"
}
]
}
And what I'm trying to do in Python is to read a JSON file, then remove an element of an array and then write the contents back to the file. I expect the file to be exactly the same, just without that element, but the problem is that when I write the contents back, they are corrupted in a weird way.
When I run this code:
import json
CONTENTS = {
"array": [
{
"foo1": "bar",
"spam1": "eggs"
},
{
"foo2": "bar",
"spam2": "eggs"
},
{
"foo3": "bar",
"spam3": "eggs"
}
]
}
# Write that object to file
with open("file.json", "w") as file:
json.dump(CONTENTS, file, indent=2)
# You can check here to see the file
input()
# Modify the file
with open("file.json", "r+") as file:
contents = json.load(file)
file.seek(0)
print(contents)
del contents["array"][-1] # Delete the last object of the array
print(contents)
json.dump(contents, file, indent=2)
The file after the second open is exactly like this:
{
"solves": [
{
"foo1": "bar",
"spam1": "eggs"
},
{
"foo2": "bar",
"spam2": "eggs"
}
]
}{
"foo3": "bar",
"spam3": "eggs"
}
]
}
As I said, I was expecting the file to be the same, just without the last object of the array, but instead it is... wrong.
Am I actually doing something wrong? I had no problem changing an object's field or appending an object to that same array in the same with block or with the same file descriptor.
My questions are: What am I doing wrong? Is the problem the fact that I read AND write to the file? How can I fix it, besides doing this:
with open("file.json", "r+") as file:
contents = json.load(file)
del contents["array"][-1] # Delete the last object of the array
with open("file.json", "w") as file:
json.dump(contents, file, indent=2)

After you overwrite the file you need to truncate it to remove the excess JSON at the end.
with open("file.json", "r+") as file:
contents = json.load(file)
file.seek(0)
print(contents)
del contents["array"][-1] # Delete the last object of the array
print(contents)
json.dump(contents, file, indent=2)
file.truncate()

Related

Separate large JSON object into many different files

I have a JSON file with 10000 data entries like below in a file.
{
"1":{
"name":"0",
"description":"",
"image":""
},
"2":{
"name":"1",
"description":"",
"image":""
},
...
}
I need to write each entry in this object into its own file.
For example, the output of each file looks like this:
1.json
{
"name": "",
"description": "",
"image": ""
}
I have the following code, but I'm not sure how to proceed from here. Can anyone help with this?
import json
with open('sample.json', 'r') as openfile:
# Reading from json file
json_object = json.load(openfile)
You can use a for loop to iterate over all the fields in the outer object, and then create a new file for each inner object:
import json
with open('sample.json', 'r') as input_file:
json_object = json.load(input_file)
for key, value in json_object.items():
with open(f'{key}.json', 'w') as output_file:
json.dump(value, output_file)

Replace json nested dictionary value with a file value

I have two files: json and text files.
I would like to replace one of the dictionary value with the value which in the text file.
Let us say, in the text file file.text, I have the following lists. [11, 15, 10].
In the json file, I have the following dictionary.
"aa": {
"bb": [
"25",
"40",
"05"
],
"cc": [
"20"
]
}
I would like to overwrite the cc value with the text file above.
file.json
"aa": {
"bb": [
"25",
"40",
"05"
],
"cc": [
"11", "15", "10"
]
}
I have tried something in Python.
def replace(text_file, json_file):
tex_file_path = 'C:/Documents/file.txt'
with open(os.path.join(tex_file_path, text_file), 'r') as f:
read_text= f.read()
json_file_path = 'C:/Documents/file.json'
with open(os.path.join(json_file_path, json_file), 'r') as f:
read_json = json.load(f)
text_to_be_replaced = read_json.get('aa')
for value in text_to_be_replaced.items():
for element in value:
# statment
I was wondering if someone can really help with this.
Although you've named it .text, the contents of the file appear to be JSON, so you can use json.load() as well. Then convert the integers in the list to strings and insert it into the desired place in the JSON file.
There's no need to loop over the dictionary items. Just address the specific element you want to replace.
def replace(text_file, json_file):
tex_file_path = 'C:/Documents'
with open(os.path.join(tex_file_path, text_file), 'r') as f:
read_text= json.load(f)
read_text = list(map(str, read_text))
json_file_path = 'C:/Documents'
with open(os.path.join(json_file_path, json_file), 'r') as f:
read_json = json.load(f)
read_json["aa"]["cc"] = read_text
with open(os.path.join(json_file_path, json_file), 'w') as f:
json.dump(read_json, f)
Also, your XXX_path variables should just be directories, the filename comes from the function parameter.
Here's a simple example using a StringIO to demonstrate reading / writing from a file-like object:
import json
from io import StringIO
json_file_obj = StringIO("""
{"aa": {
"bb": [
"25",
"40",
"05"
],
"cc": [
"20"
]
}
}
""")
text_file_obj = StringIO("[11, 15, 10]")
def replace(src_file_obj: StringIO, repl_file_obj: StringIO):
# Load file contents into a Python object
data = json.load(src_file_obj)
# Read in txt file contents
new_cc_value = json.load(repl_file_obj)
# But now result will be a list of int, here we want a list of string
new_cc_value = list(map(str, new_cc_value))
# Replace desired value
data['aa']['cc'] = new_cc_value
# Now we write to our file-like object, `src_file_obj`
# This is to demonstrate replacing the original file contents
src_file_obj = StringIO()
json.dump(data, src_file_obj)
# Seek to the start of the file
src_file_obj.seek(0)
return src_file_obj
json_file_obj = replace(json_file_obj, text_file_obj)
print(json_file_obj.read())
Output:
{"aa": {"bb": ["25", "40", "05"], "cc": ["11", "15", "10"]}}
Hint - If you want to write the output to an actual file, you can replace these lines below:
src_file_obj = StringIO()
json.dump(data, src_file_obj)
src_file_obj.seek(0)
With these lines:
with open("file_name.txt", 'w') as out_file:
json.dump(data, out_file)

Generate JSON file from file names and their content

I'm new to python, and couldn't find a close enough answer to make me figure it out. I'm trying generate a single json file that contains current directory file names that end with a .txt extension as nodes, and the contents of those files as a list inside the file name's node.
for example:
node1.txt contains
foo
bar
and node2.txt contains
test1
test2
the output should look like:
{
"node1": [
"foo",
"bar"
],
"node2": [
"test1",
"test2"
]
}
Use pathlib and json modules and a simple loop...
import pathlib
import json
data = {}
for node in pathlib.Path('.').glob('node*.txt'):
with open(node, 'r') as fp:
data[node.stem] = [line.strip() for line in fp.readlines()]
print(json.dumps(data, indent=4))
Output:
{
"node1": [
"foo",
"bar"
],
"node2": [
"test1",
"test2"
]
}

How do I maintain the same structure when reading from, modifying and writing back to a JSON file?

I am currently reading in a JSON file, adding a key and writing it back out to the same file using this procedure
with open('data.json', 'r+') as f:
data = json.load(f)
temp_key={"test":"val"}
data["test"]["new_key"] = temp_key
f.seek(0) # <--- should reset file position to the beginning.
json.dump(data, f, indent=2)
f.truncate() # remove remaining part
(adopted from here)
but the issue is that it does not maintain order. for instance if I read in:
{
"test": {
"something": "something_else"
},
"abc": {
"what": "huh"
}
}
the output turns out as:
{
"abc": {
"what": "huh"
},
"test": {
"something": "something_else",
"new_key": {
"test": "val"
}
}
}
When I would like it to be:
{
"test": {
"something": "something_else",
"new_key": {
"test": "val"
}
},
"abc": {
"what": "huh"
}
}
I realise that JSON is a key/value based structure and the order does not matter, but is there a way of making the modification and maintaining the original structure?
As I said in a comment, you can use a collections.OrderedDict along with the optional object_pairs_hook keyword argument accepted by json.load() (in Python 2.7) to preserve the order of the original data when you rewrite the file.
This is what I meant:
#!/usr/bin/env python2
from collections import OrderedDict
import json
with open('ordered_data.json', 'r+') as f:
data = json.load(f, object_pairs_hook=OrderedDict)
temp_key = {"test": "val"}
data["test"]["new_key"] = temp_key
f.seek(0) # Reset file position to the beginning.
json.dump(data, f, indent=2)
f.truncate() # Remove remaining part.

Python JSON add Key-Value pair

I'm trying to add key value pairs into the existing JSON file. I am able to concatenate to the parent label, How to add value to the child items?
JSON file:
{
"students": [
{
"name": "Hendrick"
},
{
"name": "Mikey"
}
]
}
Code:
import json
with open("input.json") as json_file:
json_decoded = json.load(json_file)
json_decoded['country'] = 'UK'
with open("output.json", 'w') as json_file:
for d in json_decoded[students]:
json.dump(json_decoded, json_file)
Expected Results:
{
"students": [
{
"name": "Hendrick",
"country": "UK"
},
{
"name": "Mikey",
"country": "UK"
}
]
}
You can do the following in order to manipulate the dict the way you want:
for s in json_decoded['students']:
s['country'] = 'UK'
json_decoded['students'] is a list of dictionaries that you can simply iterate and update in a loop. Now you can dump the entire object:
with open("output.json", 'w') as json_file:
json.dump(json_decoded, json_file)
import json
with open("input.json", 'r') as json_file:
json_decoded = json.load(json_file)
for element in json_decoded['students']:
element['country'] = 'UK'
with open("output.json", 'w') as json_out_file:
json.dump(json_decoded, json_out_file)
opened a json file i.e. input.json
iterated through each of its element
add a key named "country" and dynamic value "UK", to each element
opened a new json file with the modified JSON.
Edit:
Moved writing to output file inside to first with segment. Issue with earlier implemenation is that json_decoded will not be instantiated if opening of input.json fails. And hence, writing to output will raise an exception - NameError: name 'json_decoded' is not defined
This gives [None, None] but update the dict:
a = {'students': [{'name': 'Hendrick'}, {'name': 'Mikey'}]}
[i.update({'country':'UK'}) for i in a['students']]
print(a)

Categories

Resources