Search code examples
c++yamlyaml-cpp

How to modify a value with yaml-cpp?


I'm trying to make configuration file for my application, for which I'm using yaml-cpp library to generate a configuration and modify when user changes changes some setting or something in the application. I have a separate class set up for this, the constructor generates the yaml file which looks like this,

Serializer::Serializer(const std::string& filepath)
{
    std::ifstream ifin(filepath);

    if (!ifin)
    {
        emitter << YAML::Comment("Hello");
        emitter << YAML::BeginDoc;
        emitter << "This is the configuration file for the Sample Browser,"
                << YAML::Newline;
        emitter << "feel free to edit this file as needed";
        emitter << YAML::EndDoc;

        emitter << YAML::BeginMap;

        emitter << YAML::Newline << YAML::Key << "Window";
        emitter << YAML::BeginMap;
        emitter << YAML::Key << "SizeW" << YAML::Value << "1280";
        emitter << YAML::Key << "SizeH" << YAML::Value << "720";
        emitter << YAML::EndMap << YAML::Newline;

        emitter << YAML::Newline << YAML::Key << "Media";
        emitter << YAML::BeginMap;
        emitter << YAML::Key << "Autoplay" << YAML::Value << "false";
        emitter << YAML::Key << "Loop" << YAML::Value << "false";
        emitter << YAML::Key << "Muted" << YAML::Value << "false";
        emitter << YAML::EndMap << YAML::Newline;

        emitter << YAML::Newline << YAML::Key << "Display";
        emitter << YAML::BeginMap;
        emitter << YAML::Key << "Font";
        emitter << YAML::BeginMap;
        emitter << YAML::Key << "Family" << YAML::Value << "Sans";
        emitter << YAML::Key << "Size" << YAML::Value << "10";
        emitter << YAML::EndMap;
        emitter << YAML::EndMap << YAML::Newline;

        emitter << YAML::Newline << YAML::Key << "Import_dir";
        emitter << YAML::BeginMap;
        emitter << YAML::Key << "AutoImport" << YAML::Value << "false";
        emitter << YAML::Key << "Directory" << YAML::Value << "/home/apoorv";
        emitter << YAML::EndMap << YAML::Newline;

        emitter << YAML::EndMap;

        std::ofstream ofout(filepath);
        ofout << emitter.c_str();
    }
    else
    {
        wxLogDebug("Config file already exists! Skipping..");
    }
}

and the output of this file looks like,

# Hello
---
This is the configuration file for the Sample Browser,
feel free to edit this file as needed
...

Window:
  SizeW: 1280
  SizeH: 720

Media:
  Autoplay: false
  Loop: false
  Muted: false

Display:
  Font:
    Family: "Sans"
    Size: "10"

Import_dir:
  AutoImport: false
  Directory: "/home/apoorv/"

I want that when user changes say font, the related key value also changes with the selected option. I tried making a function that should do this like,

void Serializer::SerializeDisplaySettings(const std::string& filepath, wxFont& font)
{
    YAML::Emitter out;

    std::string fontFace = font.GetFaceName().ToStdString();
    int fontSize = font.GetPointSize();

    std::ifstream ifin(filepath);

    try
    {
        YAML::Node config = YAML::LoadAllFromFile(filepath)[1];

        auto display = config["Display"];

        if (auto fontSetting = display["Font"])
        {
            wxLogDebug("Changing font settings");
            wxLogDebug("Font face: %s", fontFace);
            wxLogDebug("Font size: %d", fontSize);

            out << YAML::Key << fontSetting["Family"] << YAML::Value << fontFace;
            out << YAML::Key << fontSetting["Size"] << YAML::Value << fontSize;

            std::ofstream ofout(filepath);
            ofout << out.c_str();
        }
        else
        {
            wxLogDebug("Error! Cannot fetch values.");
        }
    }

    catch(const YAML::ParserException& ex)
    {
        std::cout << ex.what() << std::endl;
    }
}

but this deletes the whole and fills just font name and size. How do I modify those values without changing/deleting whole file?


Solution

  • You override the file, of course it only contains the items you gave to the emitter out. If you want to modify the loaded file, you should update the values in config and write all of it back out:

    fontSetting["Family"] = fontFace;
    fontSetting["Size"] = fontSize;
    out << config;
    std::ofstream ofout(filepath);
    ofout << out.c_str();
    

    To get the other content as well, do this when loading:

    auto docs = YAML::LoadAllFromFile(filepath);
    out << YAML::Comment("Hello") << YAML::BeginDoc << docs[0] << YAML::EndDoc;
    YAMl::Node config = docs[1];