4

I would like to update existing json file.

This is example json file:

{
   "Foo": 51.32,
   "Number": 100,
   "Test": "Test1"
}

Logs from program:

Operation successfully performed
100
"Test1"
51.32
46.32
Done

Looks like everythink works as expected...

If I change fstream to ifstream to read and later ofstream to write it's working...

I tried use debugger and as I see I have wrong data in basic_ostream object... but I dont know why, I use data from string with corrected (updated data). Any idea what is wrong :-) ?

  • The right tool to solve such problems is your debugger. You should step through your code line-by-line *before* asking on Stack Overflow. For more help, please read [How to debug small programs (by Eric Lippert)](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). At a minimum, you should \[edit] your question to include a [Minimal, Complete, and Verifiable](http://stackoverflow.com/help/mcve) example that reproduces your problem, along with the observations you made in the debugger. – πάντα ῥεῖ Sep 11 '16 at 13:05
  • 1
    You will need to use `clear()`, and most likely `seek()` before attempting to rewrite its contents. Note that if the new contents are shorter than the existing contents, this will not truncate the file's size, and you will end up with trailing garbage at the end. – Sam Varshavchik Sep 11 '16 at 13:27
  • 1
    Post updated :-) – user3490777 Sep 11 '16 at 13:31

1 Answers1

1

You have a few problems here.

First the command json json_data(fs); reads to the end of the file setting the EOF flag. The stream will stop working until that flag is cleared.

Second the file pointer is at the end of the file. If you want to overwrite the file you need to move back to the beginning again:

if (fs.is_open())
{
    json json_data(fs); // reads to end of file
    fs.clear(); // clear flag
    fs.seekg(0); // move to beginning

Unfortunately that still doesn't fix everything because if the file you write back is smaller than the one you read in there will be some of the old data tagged to the end of the new data:

    std::cout << "Operation successfully performed\n";
    std::cout << json_data.at("Number") << std::endl;
    std::cout << json_data.at("Test") << std::endl;
    std::cout << json_data.at("Foo") << std::endl;

    json_data.at("Foo") = 4.32; // what if new data is smaller?

Json file:

{
   "Foo": 4.32, // this number is smaller than before
   "Number": 100,
   "Test": "Test1"
}} // whoops trailing character from previous data!!

In this situation I would simply open one file for reading then another for writing, its much less error prone and expresses the intention to overwrite everything.

Something like:

#include "json.hpp"
#include <iostream>
#include <fstream>
#include <string>

using json = nlohmann::json;

void readAndWriteDataToFile(std::string fileName) {

    json json_data;

    // restrict scope of file object (auto-closing raii)
    if(auto fs = std::ifstream(fileName))
    {
        json_data = json::parse(fs);

        std::cout << "Operation successfully performed\n";
        std::cout << json_data.at("Number") << std::endl;
        std::cout << json_data.at("Test") << std::endl;
        std::cout << json_data.at("Foo") << std::endl;
    }
    else
    {
        throw std::runtime_error(std::strerror(errno));
    }

    json_data.at("Foo") = 4.32;
    std::cout << json_data.at("Foo") << std::endl;
    std::string json_content = json_data.dump(3);

    if(auto fs = std::ofstream(fileName))
    {
        fs.write(json_content.data(), json_content.size());
        std::cout << "Done" << std::endl;
    }
    else
    {
        throw std::runtime_error(std::strerror(errno));
    }

}

int main()
{
    try
    {
        std::string fileName = "C:/new/json1.json";
        readAndWriteDataToFile(fileName);
    }
    catch(std::exception const& e)
    {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
Galik
  • 47,303
  • 4
  • 80
  • 117