c++ - 遍历 boost 属性树时变量变空

标签 c++ json c++11 boost tree

我知道我似乎有自己回答问题的习惯,但到目前为止我一直停留在这个问题上,需要一些帮助。

我编写了一些代码来将 json 格式的文件加载到类系统中。我把所有的代码放在这里: https://github.com/tomzooi/readreq

简而言之,我想做的是: 首先,我创建了一些代码,可以读取“需求”文件并使用 class.h 中的 Requirement 类存储它 同样,我可以以人类可读的格式输出以进行筛选或将其存储在 json 文件中。这行得通。

然后我希望能够读取一个 JSON 文件并再次使用相同的 Requirement 对象将其存储在内存中,但是这并不是很好(到目前为止)。

现在的主要问题是我正在遍历属性树的部分,这主要是在这个递归函数中完成的:

    void display(const int depth, const boost::property_tree::ptree& tree, Requirement * cur_requirement, std::vector<Requirement> &requirements) { 
    unsigned int count;
   std::string label,level,description;
   boost::property_tree::ptree kids = tree.get_child("");
    bool godown = false;
    for (const auto& v : kids) { // v is of type ptree::value_type
        std::cout << std::string("").assign(depth+1,'#') << " ";
        std::string nodestr = tree.get<std::string>(v.first);  
        //std::cout << v.first << " = " << nodestr << std::endl;
        if (v.first == "label") {
            label = nodestr;
            std::cout << "lbl: " << label << std::endl;
        }
        else if(v.first == "level") {
            //std::cout << "LABEL!";
                level = nodestr;
                std::cout << "lvl: " << level << std::endl;
        }
        else if(v.first == "description") {
                description = nodestr;
                std::cout << "dsc: " << description << std::endl;
        }
        else if(v.first == "children") { //going down, store stuff first
            if(depth == 0) { //zero depth
                std::cout << "zero depth...";
                requirements.emplace_back(level, description, label,cur_requirement);
                cur_requirement = &requirements.back();
            }
            else { //one or higher depth
                std::cout << "at depth " << depth << "..." << std::flush; 
                cur_requirement->children.emplace_back(level,description,label,cur_requirement->parent);
                cur_requirement = &cur_requirement->children.back();
            }
            std::cout << "going down" << std::endl;
            //cur_requirement = &cur_requirement->children.back();
            display(depth+1, v.second, cur_requirement,requirements);
        }
        else if(v.first == "") {
            std::cout << "empty v.first ... level: " << level << std::endl;
            if(depth == 0) { //zero depth
                std::cout << "store at zero depth...";
                requirements.emplace_back(level, description, label,cur_requirement);
                cur_requirement = &requirements.back();
            }
            else { //one or higher depth
                std::cout << "store at depth " << depth << " : " << level << "--" << description << std::flush; 
                cur_requirement->children.emplace_back(level,description,label,cur_requirement->parent);
                //cur_requirement = &cur_requirement->children.back();
            }
            std:: cout << " going to next " << std::endl;
            //cur_requirement = &cur_requirement->children.back();
            display(depth, v.second, cur_requirement,requirements);
        }
        else {
            std:: cout << "what else..." << std::endl;
            }
     // v.first is the name of the child
    // v.second is the child tree
    }
};  

我目前得到的输出是这样的:

[tom@tomtop dev]$ ./readreq The_system.F.req.json 
name: The system prefix: F

# lvl: should
# dsc: very well performance wise
# lbl: goperf
# zero depth...going down
## empty v.first ... level: 
store at depth 1 : -- going to next 
## lvl: should
## dsc: be listening to spaces as well
## lbl: lisspace
## empty v.first ... level: 
store at depth 1 : -- going to next 
## lvl: will
## dsc: a lot of levels back down again
## at depth 1...going down
### empty v.first ... level: 
store at depth 2 : -- going to next 
### lvl: empty
### dsc: empty
### lbl: empty
### at depth 2...going down
#### empty v.first ... level: 
store at depth 3 : -- going to next 
#### lvl: can
#### dsc: skip all the way back here
#### lbl: skiphere
#### empty v.first ... level: 
store at depth 3 : -- going to next 
#### lvl: can
#### dsc: take three linestr
#### lbl: threelines


level: should description:very well performance wise label: goperf
    level:  description: label: 
    level:  description: label: 
    level: will description:a lot of levels back down again label: 
        level:  description: label: 
        level: empty description:empty label: empty
            level:  description: label: 
            level:  description: label: 

其中大部分是有道理的,而且大部分似乎都有效,但有一件事让我感到困惑。属性树的组织方式是在每个“子节点”之前以及数组元素之间都有一个“空”节点。 (如果我错了,请纠正我,我对属性树不太熟悉)。

因此,在遇到“children”或“”(空)元素后,我想存储我之前收集的数据,存储在变量级别、描述和标签中。

这是有趣的部分,当元素是“children”时,这就像一个魅力,然而,当元素是“”时,突然变量为空,即使变量没有重新初始化,我也没有深入属性树,我只迭代到 for 循环中的下一个“ child ”。

所以我希望输出是这样的:

## lvl: should
## dsc: be listening to spaces as well
## lbl: lisspace
## empty v.first ... level: should
store at depth 1 : should -- be listening to spaces as well going to next 

最后一行(由

生成

std::cout << "empty v.first ... level: " << level << std::endl; std::cout << "store at depth " << depth << " : " << level << "--" << description << std::flush;

) 显示这个:

store at depth 1 :  --  going to next 

给人的印象是标签、描述和级别不知何故是空的,而且没有任何地方可以使它们变空。

因此,如果有人能向我解释这种神秘行为,我将非常高兴。

最佳答案

我试了 15 分钟。我无法弄清楚你想要达到的目标。 查看更新

注释

  • cur_requirement 应该被初始化。
  • 您在这里调用 UB:

    requirements.emplace_back(level, description, label, cur_requirement);
    cur_requirement = &requirements.back();
    

    您正在将指向 vector 元素的指针存储在已放置的要求中。但是,放置可能会重新分配,从而使所有指针和迭代器失效。

Rethink your data model (prefer value-semantics? use containers with stable iterators? reserve up-front?)

更新

所以,这是我对清理 display 函数的看法(我将其重命名为 parse_json,因为它就是这样做的):

void parse_json(int depth, boost::property_tree::ptree const& tree, Requirement& cur)
{
    cur.label       = tree.get("label",       "");
    cur.level       = tree.get("level",       "");
    cur.description = tree.get("description", "");

    if (auto kids = tree.get_child_optional("children")) {
        for (auto& kid : *kids) {
            std::cout << "at depth " << depth << "... " << std::flush;

            cur.children.emplace_back(&cur);

            std::cout << "going down" << std::endl;
            parse_json(depth + 1, kid.second, cur.children.back());
        }
    }
}

完整演示

查看清理后的整个程序。请注意,我已将 vector 替换为 list 以避免在读取更多子节点时使父指针无效

Live On Coliru

#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <list>

class Requirement {
    public:
        bool empty;
        std::string level;
        std::string description;
        std::string label;
        Requirement const* parent;
        std::list <Requirement> children;

        Requirement(Requirement const* p);
        Requirement(std::string l, std::string d, std::string la, Requirement const* p); // unused

        void print(std::string indent = "");
        void print_json(std::ostream &os, std::string indent = "");
};

Requirement::Requirement(Requirement const* p) 
    : empty(false), parent(p)
{
}

Requirement::Requirement(std::string l, std::string d, std::string la,Requirement const* p) // unused
    : empty(false), 
      level(std::move(l)), description(std::move(d)), label(std::move(la)), parent(p)
{
}

void Requirement::print_json(std::ostream &os, std::string indent) {
    os  << "{";
    indent += '\t';

    os
         << "\n" << indent << "\"level\":\""       << level       << "\", "
         << "\n" << indent << "\"description\":\"" << description << "\"";

    if(label.length() > 1) {
        os << ",\n" << indent << "\"label\":\"" << label <<"\"";
    }

    if (!children.empty()) {
        os << ", \"children\":[\n";

        bool first = true;
        for(auto& child : children) {
            if (!first)
                os << ',';

            first=false;

            os << "\n" << indent;
            child.print_json(os, indent);
        }
        os << "]";
    }

    indent.resize(indent.size() - 1);
    os << "\n" << indent << "}";
}

void Requirement::print(std::string indent) {
    std::cout << indent << "level: " << level << " description:" << description << " label: " << label << std::endl;
    for (Requirement kid : children)
        kid.print(indent + '\t');
}

void parse_json(int depth, boost::property_tree::ptree const& tree, Requirement& cur)
{
    cur.label       = tree.get("label",       "");
    cur.level       = tree.get("level",       "");
    cur.description = tree.get("description", "");

    if (auto kids = tree.get_child_optional("children")) {
        for (auto& kid : *kids) {
            std::cout << "at depth " << depth << "... " << std::flush;

            cur.children.emplace_back(&cur);

            std::cout << "going down" << std::endl;
            parse_json(depth + 1, kid.second, cur.children.back());
        }
    }
}

int main(int argc, char* argv[])
{
    if (argc>1) try {
        std::ifstream ss(argv[1]);

        boost::property_tree::ptree pt;
        boost::property_tree::read_json(ss, pt);

        Requirement root(nullptr);
        parse_json(0, pt, root);

        std::cout << std::endl << std::endl;
        root.print("; debug: ");
        root.print_json(std::cout);
    }
    catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}

输出是:

at depth 0... going down
at depth 0... going down
at depth 1... going down
at depth 2... going down
at depth 2... going down


; debug: level: should description:very well performance wise label: goperf
; debug:    level: should description:be listening to spaces as well label: lisspace
; debug:    level: will description:a lot of levels back down again label: 
; debug:        level: empty description:empty label: empty
; debug:            level: can description:skip all the way back here label: skiphere
; debug:            level: can description:take three linestr label: threelines
{
    "level":"should", 
    "description":"very well performance wise",
    "label":"goperf", "children":[

    {
        "level":"should", 
        "description":"be listening to spaces as well",
        "label":"lisspace"
    },
    {
        "level":"will", 
        "description":"a lot of levels back down again", "children":[

        {
            "level":"empty", 
            "description":"empty",
            "label":"empty", "children":[

            {
                "level":"can", 
                "description":"skip all the way back here",
                "label":"skiphere"
            },
            {
                "level":"can", 
                "description":"take three linestr",
                "label":"threelines"
            }]
        }]
    }]
}

请注意,代码大约只有一半大小:)

关于c++ - 遍历 boost 属性树时变量变空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29227355/

相关文章:

c++ - 使用递归从尾部开始反转链表

javascript - 如何浏览一个javascript对象

json - jq:按值惯用地聚合对象键

c++ - 为什么这段将字符串初始化为单个字符的代码会调用 initializer_list 构造函数?

c++ - 在 C++ 0x 中打开枚举类

c++ - 将二进制数据 (std::string) 写入 std::ofstream?

c++ - 标准 C++ 线程 ID - 奇怪的行为

c++ - 通过引用传递指向 const 对象的指针

java - 使用 Jackson 进行序列化和反序列化 : how to programmatically ignore fields?

c++ - && 声明的变量