【问题标题】:Creating JSON arrays in Boost using Property Trees使用属性树在 Boost 中创建 JSON 数组
【发布时间】:2011-01-08 00:56:24
【问题描述】:

我正在尝试使用 boost 属性树创建一个 JSON 数组。

documentation 表示:“JSON 数组映射到节点。每个元素都是一个空名称的子节点。”

所以我想创建一个名称为空的属性树,然后调用write_json(...) 将数组取出。但是,文档并没有告诉我如何创建未命名的子节点。我试过ptree.add_child("", value),但这会产生:

Assertion `!p.empty() && "Empty path not allowed for put_child."' failed

文档似乎没有解决这一点,至少我无法弄清楚。有人可以帮忙吗?

【问题讨论】:

    标签: c++ json boost boost-propertytree


    【解决方案1】:

    简单数组:

    #include <boost/property_tree/ptree.hpp>
    using boost::property_tree::ptree;
    
    ptree pt;
    ptree children;
    ptree child1, child2, child3;
    
    child1.put("", 1);
    child2.put("", 2);
    child3.put("", 3);
    
    children.push_back(std::make_pair("", child1));
    children.push_back(std::make_pair("", child2));
    children.push_back(std::make_pair("", child3));
    
    pt.add_child("MyArray", children);
    
    write_json("test1.json", pt);
    

    结果:

    {
        "MyArray":
        [
            "1",
            "2",
            "3"
        ]
    }
    

    对象上的数组:

    ptree pt;
    ptree children;
    ptree child1, child2, child3;
    
    
    child1.put("childkeyA", 1);
    child1.put("childkeyB", 2);
    
    child2.put("childkeyA", 3);
    child2.put("childkeyB", 4);
    
    child3.put("childkeyA", 5);
    child3.put("childkeyB", 6);
    
    children.push_back(std::make_pair("", child1));
    children.push_back(std::make_pair("", child2));
    children.push_back(std::make_pair("", child3));
    
    pt.put("testkey", "testvalue");
    pt.add_child("MyArray", children);
    
    write_json("test2.json", pt);
    

    结果:

    {
        "testkey": "testvalue",
        "MyArray":
        [
            {
                "childkeyA": "1",
                "childkeyB": "2"
            },
            {
                "childkeyA": "3",
                "childkeyB": "4"
            },
            {
                "childkeyA": "5",
                "childkeyB": "6"
            }
        ]
    }
    

    希望对你有帮助

    【讨论】:

    • 请注意,数字被编码为字符串而不是数字。
    • 嗨@Luke,我遇到了这个字符串问题。如何将它们编码为数字?
    • 我在使用 boost 库时找不到好的解决方案。我改用 rapidjson。
    • 这有很多限制:你不能只用一个元素创建数组,多次重复同一个元素对我来说也不能正常工作。
    • 有一种更简单的方法可以制作不需要中间子对象的纯值数组:children.push_back(ptree::value_type("", "1"));但是,该值必须格式化为字符串
    【解决方案2】:

    你需要做的是这件有趣的事。这是凭记忆写的,但这样的东西对我有用。

    boost::property_tree::ptree root;
    boost::property_tree::ptree child1;
    boost::property_tree::ptree child2;
    
    // .. fill in children here with what you want
    // ...
    
    ptree.push_back( std::make_pair("", child1 ) );
    ptree.push_back( std::make_pair("", child2 ) );
    

    但是要注意在 json 解析和写入中存在一些错误。其中几个我已经提交了错误报告 - 没有回应:(

    编辑:解决有关它序列化错误为 {"":"","":""}

    的问题

    只有当数组是根元素时才会发生这种情况。 boost ptree 编写器将所有根元素视为对象 - 绝不是数组或值。这是由 boost/propert_tree/detail/json_parser_writer.hpp 中的以下行引起的

    else if (indent > 0 && pt.count(Str()) == pt.size())
    

    去掉“indent > 0 &&”将允许它正确地写入数组。

    如果您不喜欢产生的空间大小,可以使用我提供的补丁here

    【讨论】:

    • 这是不对的。转储到 JSON 后,我得到的是:{ "": "", "": "" }。
    • 更新了帖子以反映为什么会发生这种情况以及如何解决它。
    • 遗憾地报告,在 1.53.0 中似乎仍然无法创建数组作为根元素。
    • 你也可以尝试直接调用内部助手,伪造缩进​​。 boost::property_tree::json_parser::write_json_helper(stream, root,1,false);
    • @Gabor:你为什么要通过pretty=false 那里?它可以工作,但write_json 中的默认设置是漂亮打印。
    【解决方案3】:

    当开始使用属性树来表示 JSON 结构时,我遇到了类似的问题,但我没有解决。另请注意,从文档中,属性树不完全支持类型信息:

    JSON 值映射到包含该值的节点。但是,所有类型信息都丢失了;数字以及文字“null”、“true”和“false”都简单地映射到它们的字符串形式。

    了解了这个之后,我切换到了更完整的 JSON 实现 JSON Spirit。该库使用 Boost Spirit 实现 JSON 语法,完全支持 JSON 包括数组。

    我建议您使用替代的 C++ JSON 实现。

    【讨论】:

    • 自从我写了这个答案以来,有很多新的库。这是我最近遇到的一个不错的:github.com/nlohmann/json
    • 而 Boost 1.75.0 引入了 Boost JSON
    【解决方案4】:

    在我的情况下,我想将一个数组添加到一个或多或少的任意位置,因此,就像迈克尔的回答一样,创建一个子树并用数组元素填充它:

    using boost::property_tree::ptree;
    
    ptree targetTree;
    ptree arrayChild;
    ptree arrayElement;
    
    //add array elements as desired, loop, whatever, for example
    for(int i = 0; i < 3; i++)
    {
      arrayElement.put_value(i);
      arrayChild.push_back(std::make_pair("",arrayElement))
    }
    

    当子节点被填充后,使用put_child()add_child() 函数将整个子树添加到目标树中,像这样...

    targetTree.put_child(ptree::path_type("target.path.to.array"),arrayChild)
    

    put_child 函数将路径和树作为参数,并将 arrayChild “嫁接”到 targetTree 中

    【讨论】:

      【解决方案5】:

      截至boost 1.60.0,问题仍然存在。

      提供Python 3 解决方法 (Gist),可以在 boost::property_tree::write_json 之后进行系统调用。

      #!/usr/bin/env python3
      
      
      def lex_leaf(lf: str):
          if lf.isdecimal():
              return int(lf)
          elif lf in ['True', 'true']:
              return True
          elif lf in ['False', 'false']:
              return False
          else:
              try:
                  return float(lf)
              except ValueError:
                  return lf
      
      def lex_tree(j):
          tj = type(j)
          if tj == dict:
              for k, v in j.items():
                  j[k] = lex_tree(v)
          elif tj == list:
              j = [lex_tree(l) for l in j]
          elif tj == str:
              j = lex_leaf(j)
          else:
              j = lex_leaf(j)
          return j
      
      
      def lex_file(fn: str):
          import json
          with open(fn, "r") as fp:
              ji = json.load(fp)
          jo = lex_tree(ji)
          with open(fn, 'w') as fp:
              json.dump(jo, fp)
      
      
      if __name__ == '__main__':
          import sys
          lex_file(sys.argv[1])
      

      【讨论】:

        【解决方案6】:

        如果您想要 C++ 中的 JSON,则不需要 Boost。使用这个library,您可以将 JSON 作为第一类数据类型,其行为类似于 STL 容器。

        // Create JSON on the fly.
        json j2 = {
          {"pi", 3.141},
          {"happy", true},
          {"name", "Niels"},
          {"nothing", nullptr},
          {"answer", {
            {"everything", 42}
          }},
          {"list", {1, 0, 2}},
          {"object", {
            {"currency", "USD"},
            {"value", 42.99}
          }}
        };
        
        // Or treat is as an STL container; create an array using push_back
        json j;
        j.push_back("foo");
        j.push_back(1);
        j.push_back(true);
        
        // also use emplace_back
        j.emplace_back(1.78);
        
        // iterate the array
        for (json::iterator it = j.begin(); it != j.end(); ++it) {
          std::cout << *it << '\n';
        }
        

        【讨论】:

          【解决方案7】:

          对官方文档和上面的答案感到困惑。 以下是我的理解。

          属性树由节点组成。
          每个节点如下所示

           struct ptree
              {
                 map<key_name,value>          data;
                 vector<pair<key_name,ptree>> children; 
              };
          

          使用“put”将“值”放入数据中
          使用 'push_back' 将 'node' 放入孩子中\

          // Write 
              bt::ptree root;
              bt::ptree active;
              bt::ptree requested;
              bt::ptree n1, n2, n3;
                  
              n1.put("name", "Mark");
              n1.put("age", 20);
              n1.put("job", "aaa");
          
              n2.put("name", "Rosie");
              n2.put("age", "19");
              n2.put("job", "bbb");
          
              n3.put("name", "sunwoo");
              n3.put("age", "10");
              n3.put("job", "ccc");
          
              active.push_back   ({ "",l1 });
              active.push_back   ({ "",l2 });
              requested.push_back({ "",l3 }); 
              root.push_back     ({"active", active});
              root.push_back     ({"requested", requested});
          
              bt::write_json("E:\\1.json", root);
          
          
              // READ
              bt::ptree root2;
              bt::ptree active2;
              bt::ptree requested2;
              bt::ptree r1, r2, r3;
          
              bt::read_json("E:\\1.json", root2);
          
              // loop children
              for (auto& [k,n] : root.get_child("active"))
              {       
                  cout << n.get<string>("name", "unknown");
                  cout << n.get<int>   ("age" , 11);
                  cout << n.get<string>("job" , "man");
                  cout << endl << flush;
              }
          

          【讨论】:

            猜你喜欢
            • 2020-02-02
            • 2012-12-07
            • 1970-01-01
            • 2014-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多