如何在yaml-cpp中合并节点

时间:2016-12-26 03:15:59

标签: yaml-cpp

我有两个节点对象,如下所示:

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

我想合并它,结果如下:

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

如何合并?当节点大小和深度没有显示时。

2 个答案:

答案 0 :(得分:2)

这是我的尝试:

#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;
}

对于非标量键,我选择忽略节点等效。请注意,此版本不会修改a。它会返回一个新地图c,它是ba的合并。 b中的值将替换a地图中c的同名键控非地图值。

答案 1 :(得分:0)

我发现 md5i 的回答存在一个问题,即它不会合并第二个节点的唯一子节点。我的修复是在节点 b 的 for 循环中再次调用该函数(我将其重命名为覆盖节点)。我还把所有东西都做成了常量,因为我没有在这里编辑任何东西,所以我不必投射它。我还强调了第二个节点覆盖另一个节点的事实。

const YAML::Node mergeNodes(const YAML::Node& defaultNode, const YAML::Node& overrideNode)
{
  if (!overrideNode.IsMap()) {
    // If overrideNode is not a map, merge result is overrideNode, unless overrideNode is null
    return overrideNode.IsNull() ? defaultNode : overrideNode;
  }
  if (!defaultNode.IsMap()) {
    // If defaultNode is not a map, merge result is overrideNode
    return overrideNode;
  }
  if (!defaultNode.size()) {
    return YAML::Node(overrideNode);
  }
  // Create a new map 'newNode' with the same mappings as defaultNode, merged with overrideNode
  auto newNode = YAML::Node(YAML::NodeType::Map);
  for (auto node : defaultNode) {
    if (node.first.IsScalar()) {
      const std::string& key = node.first.Scalar();
      if (overrideNode[key]) {
        newNode[node.first] = mergeNodes(node.second, overrideNode[key]);
        continue;
      }
    }
    newNode[n.first] = node.second;
  }
  // Add the mappings from 'overrideNode' not already in 'newNode'
  for (auto node : overrideNode) {
    if (!node.first.IsScalar()) {
      const std::string& key = node.first.Scalar();
      if (defaultNode[key]) {
        newNode[node.first] = mergeNodes(defaultNode[key], node.second);
        continue;
      }
    }
    newNode[node.first] = node.second;
  }
  return YAML::Node(newNode);
}

这可能不会对 N 事情有任何好处,但它会从节点 b/overrideNode 获取节点,即使它们在节点 a/defaultNode 中没有实例。