通过 boost 属性树(递归)发出 YAML 迭代

Posted

技术标签:

【中文标题】通过 boost 属性树(递归)发出 YAML 迭代【英文标题】:Emit YAML iterating through boost property tree (recursively) 【发布时间】:2018-04-19 13:16:02 【问题描述】:

我有一个小而复杂的树结构。使用,提升属性树作为容器,我试图遍历树,然后使用 yaml-cpp 库将其发送到 yaml 文件。

例如,我有一个小的嵌套属性树:

fibonacci:
  type: series
  entities:
    golden_ratio:
      ratio: 2.3
    function:
      power_series: 2

我希望我的 yaml 文件看起来像这样。

我编写了一个递归函数来遍历树并发送到 yaml。

//Member variable
YAML::Emitter m_out

void iterator(const boost::property_tree::ptree& tree, const std::string& key)

    for (const auto& item: tree)
    
        if (item.second.data().empty()) //check if map node
        
            m_out << YAML::BeginMap;
            m_out << YAML::Key << item.first;
        
        else if (!item.second.data().empty()) //else it is key/value pair
        
            m_out << YAML::Key << item.first;
            m_out << YAML::Value << item.second.data();
        
        if (!item.second.empty()) //If the node has child
        
            iterator(item.second, item.first);
        
    

我使用 emtpy 键作为 iterator(root, "") 调用该函数。我知道属性树作为键/值对工作,而 Yaml-cpp 具有节点名称。在代码中,我只是试图根据值假设树节点的类型(无值 - 映射节点,否则 - 键/值节点)

显然,由于我的逻辑错误,我发出的 yaml 文件不具备上述所需的树结构。我想做一个递归函数,它可以遍历任何类型的树并将其发送到 yaml 文件。 是否可以迭代树并随后递归地发送到 yaml?如果是的话,我会很感激一些想法。

【问题讨论】:

【参考方案1】:

因此,我采用了您想要的 YAML 并将其通过 online converter 获得“可靠”的 ptree 表示(有趣的是,您忽略了这个问题)。

然后我开始进行简单的 ptree 往返以进行完整性检查:

Live On Coliru

#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;

std::istringstream sample_json();
ptree sample_ptree();

int main() 
    write_json(std::cout, sample_ptree());


std::istringstream sample_json() 
    return std::istringstream(R"(
        "fibonacci": 
            "type": "series",
            "entities": 
                "golden_ratio": 
                    "ratio": 2.3
                ,
                "function": 
                    "power_series": 2
                
            
        
    )");


ptree sample_ptree() 
    ptree pt;
    
        auto stream = sample_json();
        read_json(stream, pt);
    
    return pt;

打印


    "fibonacci": 
        "type": "series",
        "entities": 
            "golden_ratio": 
                "ratio": "2.3"
            ,
            "function": 
                "power_series": "2"
            
        
    

to_yaml #1

最简单的当然是读取同一个JSON,然后让yaml-cpp做转换:

auto stream = sample_json();
std::cout << YAML::Load(stream) << "\n";

打印:

fibonacci: type: series, entities: golden_ratio: ratio: 2.3, function: power_series: 2

to_yaml 采取#2:漂亮的打印

首先

命名很重要。 iterator 没有描述功能,与well-known concept from the standard library 冲突 key 参数未使用 你只有BeginMap,如果你在代码中没有EndMap,你怎么期待一棵有效的树? 请不要使用全局变量。它们使您的代码变得脆弱(非确定性、非幂等、不可重入、非线程安全等)。只需将 Emitter&amp; 作为参数传递即可。

我会让它更简单:

void to_yaml(ptree const& node, YAML::Emitter &m_out) 
    if (node.empty()) 
        m_out << YAML::Value << node.data(); 
     else 
        m_out << YAML::BeginMap;
        for (auto const&item : node) 
            m_out << YAML::Key << item.first;
            to_yaml(item.second, m_out);
        
        m_out << YAML::EndMap;
    

现在,为了有一个方便的入口点,添加一个重载:

std::string to_yaml(ptree const& tree) 
    YAML::Emitter out;
    to_yaml(tree, out);
    return out.c_str();

现在您可以通过以下方式打印结果:

std::cout << to_yaml(sample_ptree()) << "\n";

打印:

fibonacci:
  type: series
  entities:
    golden_ratio:
      ratio: 2.3
    function:
      power_series: 2

完整列表

#include <iostream>

#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;

std::istringstream sample_json();
ptree sample_ptree();

#include "yaml-cpp/yaml.h"

void to_yaml(ptree const& node, YAML::Emitter &m_out) 
    if (node.empty()) 
        m_out << YAML::Value << node.data(); 
     else 
        m_out << YAML::BeginMap;
        for (auto const&item : node) 
            m_out << YAML::Key << item.first;
            to_yaml(item.second, m_out);
        
        m_out << YAML::EndMap;
    


std::string to_yaml(ptree const& tree) 
    YAML::Emitter out;
    to_yaml(tree, out);
    return out.c_str();


int main() 
    write_json(std::cout, sample_ptree());

    
        auto stream = sample_json();
        std::cout << YAML::Load(stream) << "\n";
    

    std::cout << to_yaml(sample_ptree()) << "\n";


std::istringstream sample_json() 
    return std::istringstream(R"(
        "fibonacci": 
            "type": "series",
            "entities": 
                "golden_ratio": 
                    "ratio": 2.3
                ,
                "function": 
                    "power_series": 2
                
            
        
    )");


ptree sample_ptree() 
    ptree pt;
    
        auto stream = sample_json();
        read_json(stream, pt);
    
    return pt;

【讨论】:

感谢您写得非常好!它消除了我的许多困惑。我希望它会帮助很多其他人。

以上是关于通过 boost 属性树(递归)发出 YAML 迭代的主要内容,如果未能解决你的问题,请参考以下文章

通过使用 boost 属性树解析 JSON 文件来访问布尔值

如何使用柯南安装 Boost 属性树?

使用 boost 属性树遍历 json 属性键和值

让 static_visitor 在遍历 Boost 递归变体时对其进行修改?

Boost 属性树:删除嵌套节点

如何让boost属性树打印所有子树然后结束父树