我知道我似乎有习惯回答自己的问题,但是到目前为止,我仍然坚持这一问题,需要一些帮助。

我已经编写了一些代码以将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:

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

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

这是一个有趣的部分,当元素是“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

最后一行(由



)显示以下内容:
store at depth 1 :  --  going to next

给人的印象是标签,描述和级别在什么地方是空的,而没有地方可以使它们为空。

因此,如果有人可以向我解释这种愚蠢的行为,我将非常高兴。

最佳答案

我尝试了15分钟。我无法解决您要达到的目标。
查看更新

笔记

  • cur_requirement应该被初始化。
  • 您在这里调用UB:
    requirements.emplace_back(level, description, label, cur_requirement);
    cur_requirement = &requirements.back();
    

    您将在放置的需求中存储指向 vector 元素的指针。但是,嵌入可能会重新分配,使的所有指针和迭代器无效。



  • 更新

    因此,这是我的工作,需要清理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"
                }]
            }]
        }]
    }
    

    请注意,代码的大小大约是一半:)

    10-02 08:25