The problem is that just writing the data at the end of the file is not enough to do what you need.
When writing at the end of the file with the option 'a'
, you are only writing the content of the new question, ignoring the semantics of the file content (actually the new question should be added to the existing JSON object, instead of being written soon after).
That is, the file already has a JSON object containing several questions. Instead of adding a question to this object, what you did was write another object right after. That’s why the file looked like this:
{
"PERGUNTA 1": "RESPOSTA",
"PERGUNTA 2": "RESPOSTA",
"PERGUNTA 3": "RESPOSTA",
"PERGUNTA 4": "RESPOSTA",
}{
"PERGUNTA 5": "RESPOSTA"
}
And if you try to read it again, it will be an error, because this is not a valid JSON (since it is now 2 different objects, instead of just one: the first object has questions 1 to 4, and the second object has question 5).
But from what I understand, what you wanted was a single object containing all five questions:
{
"PERGUNTA 1": "RESPOSTA",
"PERGUNTA 2": "RESPOSTA",
"PERGUNTA 3": "RESPOSTA",
"PERGUNTA 4": "RESPOSTA",
"PERGUNTA 5": "RESPOSTA"
}
An option to resolve would be to open the file and load all its contents with json.load
, thus obtaining the respective object. Then you update this object with the new question and overwrite the file.
But instead of writing directly to the same file, I think it’s best to first write the content in a temporary file, and only in the end, if all goes well, do you rename the temporary file. Thus, if an error occurs while writing, the original file is preserved (as the error will only affect the temporary file). It would look like this:
import json
import shutil
import tempfile
with open('dados.json', 'r', encoding='utf-8') as arq, \
tempfile.NamedTemporaryFile('w', delete=False) as out:
# ler todo o arquivo e obter o objeto JSON
dados = json.load(arq)
# atualizar os dados com a nova pergunta
dados["PERGUNTA 5"] = "RESPOSTA"
# escreve o objeto atualizado no arquivo temporário
json.dump(dados, out, ensure_ascii=False, indent=4, separators=(',',':'))
# se tudo deu certo, renomeia o arquivo temporário
shutil.move(out.name, 'dados.json')
To create the temporary file, I used the module tempfile
. With this, after the code runs, the contents of the file will be:
{
"PERGUNTA 1":"RESPOSTA",
"PERGUNTA 2":"RESPOSTA",
"PERGUNTA 3":"RESPOSTA",
"PERGUNTA 4":"RESPOSTA",
"PERGUNTA 5":"RESPOSTA"
}
This method also works for other cases, such as deleting one of the questions, or updating their text (because then the file may be smaller than the original, so only writing at the end would not be the most appropriate action).
A detail, its original file nay can have the comma after question 4, otherwise it will be an invalid JSON:
{
"PERGUNTA 1": "RESPOSTA",
"PERGUNTA 2": "RESPOSTA",
"PERGUNTA 3": "RESPOSTA",
"PERGUNTA 4": "RESPOSTA", <-- esta vírgula está errada, e deve ser removida
}
Finally, if the idea is that the file should serve to persist the data, but not necessarily need to be read by a person, consider using the modules pickle
or shelve
- the latter in particular seems to be more suited to your need. An idea would be to first convert your original JSON to the shelve
:
import json
import shelve
# primeira vez, carregar o arquivo JSON e criar o arquivo no formato do shelve
with open('dados.json', 'r', encoding='utf-8') as arq, \
shelve.open('dados_shelve.json') as novos_dados:
dados = json.load(arq)
novos_dados.update(dados)
Then, just update the new file using the file itself shelve
:
# da segunda vez em diante, basta ler o arquivo com shelve
with shelve.open('dados_shelve.json') as dados:
# a linha abaixo já atualiza o arquivo
dados["PERGUNTA 5"] = "RESPOSTA"
The difference is that the shelve
creates binary files, not cute text like the JSON file. But if the idea is just to persist the data and keep updating it, without a person having to read it, this is a good alternative.
hello I was looking at your code because I was having the same difficulty as the author of the question, and I was left with a question. shutil.move(out.name, 'data.json'). This part of the code, because you used "out.name"?
– Henrique FH
@Henriquefh
out.name
is the temporary file name. Without it themove
wouldn’t know which file to rename– hkotsubo