yaml-cpp icon indicating copy to clipboard operation
yaml-cpp copied to clipboard

Empty map artifact impedes formatting of subsequent writes

Open lFatality opened this issue 4 years ago • 1 comments
trafficstars

Description

When you have a file with a map containing a list and you delete the last key in this map, an empty map artifact {} remains in the file. When you later load the file again by instantiating a new node and write to the file, this empty artifact seems to affect the formatting of the file.

How to reproduce

Here is an example that will reproduce the error. Make sure to have an empty testyaml.yaml file at the location where the filePath points to.

#include <cstdio>
#include "yaml-cpp/yaml.h"
#include <fstream>
#include <iostream>

int main() {
    std::string filePath("testyaml.yaml");
    YAML::Node data = YAML::LoadFile(filePath);

    std::ofstream fout;

    std::vector<std::string> stringData;
    stringData.push_back("a_new_value");

    // step 1: write a map with a nested list
    std::cout<< "Before step 1: First we write a new list nested in a map to an empty file. Press enter to continue." << std::endl;
    std::cin.get();

    if (stringData.size() != 0) {
        for (std::string str: stringData) {
            data["my_key"].push_back(str);
        }
    }

    fout.open(filePath);
    fout << data;
    fout.close();

    // step 2: delete the map
    std::cout<< "After step 1: We've written the map. The formatting is correct. Now we delete the last key in the map. Press enter to continue." << std::endl;
    std::cin.get();

    if (data["my_key"]) {
        data.remove("my_key");
    }

    fout.open(filePath);
    fout << data;
    fout.close();

    // step 3: repeat step 1
    std::cout<< "After step 2: The last key in the map was deleted and the file was saved. There is an empty map artifact. Now we write a new value with the same method as before with a new node. Press enter to continue." << std::endl;
    std::cin.get();

    YAML::Node data2 = YAML::LoadFile(filePath); // load the written file with a new node

    if (stringData.size() != 0) {
        for (std::string str: stringData) {
            data2["my_key"].push_back(str);
        }
    }

    fout.open(filePath);
    fout << data2;
    fout.close();

    std::cout<< "After step 3: Even though there is a list nested in a map with the correct keys, the formatting is corrupted." << std::endl;
}

The different stages of the .yaml file:

  • Before step 1: The file is empty.
  • After step 1 (writing the map):
my_key:
  - a_new_value
  • After step 2 (deleting the map):
{}
  • After step 3 (repeating step 1):
{my_key: [a_new_value]}

Expected vs. actual behavior

The expected behavior would be that deleting the last key in the last map would result in an empty file. A subsequent write would then create a nicely formatted file, similar to that which is created after step 1.

The actual behavior shows that deleting the last key in the last map results in a non-empty file, leaving an empty map artifact.
A subsequent write results in logically correct content, but the formatting is not correct.

Further notes

The behavior does not occur if you execute step 3 with the same node that was used for step 1.
I assume that this is the case because instantiating the node again reads the actual file again, not using the internally stored context in the initially created node.

lFatality avatar Apr 10 '21 22:04 lFatality

Another way to reproduce:

    YAML::Node config(YAML::NodeType::Map);
    std::ofstream fout("out.yaml");
    fout << config;
    fout.close();

Akaame avatar Aug 03 '21 21:08 Akaame