yaml-cpp

How to merge node in yaml-cpp


I have two node object, like this:

school:
  grade:
    class:
     name: bob
school:
  grade:
    class:
      age: 18

I want to merge it, the result like this:

school:
  grade:
    class:
      name: bob
      age: 18

How to merge it? when the node size and depth do not kown.


Solution

  • Here is my attempt:

    #include <yaml-cpp/yaml.h>
    
    inline const YAML::Node & cnode(const YAML::Node &n) {
        return n;
    }
    
    YAML::Node merge_nodes(YAML::Node a, YAML::Node b)
    {
      if (!b.IsMap()) {
        // If b is not a map, merge result is b, unless b is null
        return b.IsNull() ? a : b;
      }
      if (!a.IsMap()) {
        // If a is not a map, merge result is b
        return b;
      }
      if (!b.size()) {
        // If a is a map, and b is an empty map, return a
        return a;
      }
      // Create a new map 'c' with the same mappings as a, merged with b
      auto c = YAML::Node(YAML::NodeType::Map);
      for (auto n : a) {
        if (n.first.IsScalar()) {
          const std::string & key = n.first.Scalar();
          auto t = YAML::Node(cnode(b)[key]);
          if (t) {
            c[n.first] = merge_nodes(n.second, t);
            continue;
          }
        }
        c[n.first] = n.second;
      }
      // Add the mappings from 'b' not already in 'c'
      for (auto n : b) {
        if (!n.first.IsScalar() || !cnode(c)[n.first.Scalar()]) {
          c[n.first] = n.second;
        }
      }
      return c;
    }
    

    For non-scalar keys I have opted to ignore node equivalence. Please note that this version does not modify a. It returns a new map c which is a merge of b into a. Values from b will replace identically keyed non-map values from a in the c map.