【问题标题】:Reading json files in C++在 C++ 中读取 json 文件
【发布时间】:2015-11-19 06:35:11
【问题描述】:

我正在尝试读取 JSON 文件。到目前为止,我一直专注于使用 jsoncpp 库。但是,文档对我来说很难理解。任何人都可以用通俗的术语解释它的作用吗?

假设我有一个people.json,看起来像这样:

{"Anna" : { 
      "age": 18,
      "profession": "student"},
 "Ben" : {
      "age" : "nineteen",
      "profession": "mechanic"}
 }

当我读到这篇文章时会发生什么?我可以创建某种数据结构people,我可以通过AnnaBen 以及ageprofession 来索引它吗? people 的数据类型是什么?我认为它类似于(嵌套)地图,但地图值总是必须具有相同的类型,不是吗?

我以前使用过 python,我的“目标”(对于 C++ 来说可能是错误的)是获得一个嵌套的 python 字典的等价物。

【问题讨论】:

  • fwiw:我已经成功使用了这个 C++ json 解析库github.com/cierelabs/json_spirit
  • 请注意,JSON(与 JavaScript 不同)要求将键括在双引号中。
  • 我知道下面有人建议过,但是对于现在正在阅读此内容的任何人,如果您使用 C++ 编写,请认真考虑 nlohmann::json

标签: c++ json dictionary jsoncpp


【解决方案1】:

完成 Fedora 33 的最小工作示例

使用sudo dnf install jsoncpp-devel 获取库。

将以下文件存储在您的项目文件夹中:

people.json

{
    "Anna":
    { 
        "age": 18,
            "profession": "student"
        },
    "Ben":
    {
        "age" : "nineteen",
        "profession": "mechanic"
    }
}

ma​​in.cpp

#include <iostream>
#include <fstream>
#include <json/json.h>

int main()
{
    Json::Value people;
    std::ifstream people_file("people.json", std::ifstream::binary);
    people_file >> people;

    std::cout << people["Anna"] << "\n";
    std::cout << people["Anna"]["profession"] << "\n";
}

使用g++ -ljsoncpp main.cpp 编译。调用./a.out 会产生

输出

{
    "age" : 18,
    "profession" : "student"
}
"student"

【讨论】:

    【解决方案2】:

    您可以使用 c++ boost::property_tree::ptree 来解析 json 数据。这是您的 json 数据的示例。如果您在每个子节点内移动名称,这会更容易

    #include <iostream>                                                             
    #include <string>                                                               
    #include <tuple>                                                                
    
    #include <boost/property_tree/ptree.hpp>                                        
    #include <boost/property_tree/json_parser.hpp> 
     int main () {
    
        namespace pt = boost::property_tree;                                        
        pt::ptree loadPtreeRoot;                                                    
    
        pt::read_json("example.json", loadPtreeRoot);                               
        std::vector<std::tuple<std::string, std::string, std::string>> people;      
    
        pt::ptree temp ;                                                            
        pt::ptree tage ;                                                            
        pt::ptree tprofession ;                                                     
    
        std::string age ;                                                           
        std::string profession ;                                                    
        //Get first child                                                           
        temp = loadPtreeRoot.get_child("Anna");                                     
        tage = temp.get_child("age");                                               
        tprofession = temp.get_child("profession");                                 
    
        age =  tage.get_value<std::string>();                                       
        profession =  tprofession.get_value<std::string>();                         
        std::cout << "age: " << age << "\n" << "profession :" << profession << "\n" ;
        //push tuple to vector                                                      
        people.push_back(std::make_tuple("Anna", age, profession));                 
    
        //Get Second child                                                          
        temp = loadPtreeRoot.get_child("Ben");                                      
        tage = temp.get_child("age");                                               
        tprofession = temp.get_child("profession");                                 
    
        age =  tage.get_value<std::string>();                                       
        profession  =  tprofession.get_value<std::string>();                        
        std::cout << "age: " << age << "\n" << "profession :" << profession << "\n" ;
        //push tuple to vector                                                      
        people.push_back(std::make_tuple("Ben", age, profession));                  
    
        for (const auto& tmppeople: people) {                                       
            std::cout << "Child[" << std::get<0>(tmppeople) << "] = " << "  age : " 
            << std::get<1>(tmppeople) << "\n    profession : " << std::get<2>(tmppeople) << "\n";
        }  
    }
    

    【讨论】:

      【解决方案3】:

      这是另一种更容易读取 json 文件的方法:

      #include "json/json.h"
      
      std::ifstream file_input("input.json");
      Json::Reader reader;
      Json::Value root;
      reader.parse(file_input, root);
      cout << root;
      

      然后你可以得到这样的值:

      cout << root["key"]
      

      【讨论】:

      • json头文件的封装是什么?
      【解决方案4】:

      看看nlohmann's JSON Repository on GitHub。我发现这是使用 JSON 最方便的方式。

      它的设计就像一个 STL 容器,这使得它的使用非常直观。

      【讨论】:

        【解决方案5】:

        读取json配置文件的示例(附完整源代码):

        https://github.com/sksodhi/CodeNuggets/tree/master/json/config_read

         > pwd
        /root/CodeNuggets/json/config_read
         > ls
        Makefile  README.md  ReadJsonCfg.cpp  cfg.json
         > cat cfg.json 
        {
           "Note" : "This is a cofiguration file",
           "Config" : { 
               "server-ip"     : "10.10.10.20",
               "server-port"   : "5555",
               "buffer-length" : 5000
           }   
        }
         > cat ReadJsonCfg.cpp 
        #include <iostream>
        #include <json/value.h>
        #include <jsoncpp/json/json.h>
        #include <fstream>
        
        void 
        displayCfg(const Json::Value &cfg_root);
        
        int
        main()
        {
            Json::Reader reader;
            Json::Value cfg_root;
            std::ifstream cfgfile("cfg.json");
            cfgfile >> cfg_root;
        
            std::cout << "______ cfg_root : start ______" << std::endl;
            std::cout << cfg_root << std::endl;
            std::cout << "______ cfg_root : end ________" << std::endl;
        
            displayCfg(cfg_root);
        }       
        
        void 
        displayCfg(const Json::Value &cfg_root)
        {
            std::string serverIP = cfg_root["Config"]["server-ip"].asString();
            std::string serverPort = cfg_root["Config"]["server-port"].asString();
            unsigned int bufferLen = cfg_root["Config"]["buffer-length"].asUInt();
        
            std::cout << "______ Configuration ______" << std::endl;
            std::cout << "server-ip     :" << serverIP << std::endl;
            std::cout << "server-port   :" << serverPort << std::endl;
            std::cout << "buffer-length :" << bufferLen<< std::endl;
        }
         > cat Makefile 
        CXX = g++
        PROG = readjsoncfg
        
        CXXFLAGS += -g -O0 -std=c++11
        
        CPPFLAGS += \
                -I. \
                -I/usr/include/jsoncpp
        
        LDLIBS = \
                         -ljsoncpp
        
        LDFLAGS += -L/usr/local/lib $(LDLIBS)
        
        all: $(PROG)
                @echo $(PROG) compilation success!
        
        SRCS = \
                ReadJsonCfg.cpp
        OBJS=$(subst .cc,.o, $(subst .cpp,.o, $(SRCS)))
        
        $(PROG): $(OBJS)
                $(CXX) $^ $(LDFLAGS) -o $@
        
        clean:
                rm -f $(OBJS) $(PROG) ./.depend
        
        depend: .depend
        
        .depend: $(SRCS)
                rm -f ./.depend
                $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM $^ >  ./.depend;
        
        include .depend
         > make
        Makefile:43: .depend: No such file or directory
        rm -f ./.depend
        g++ -g -O0 -std=c++11 -I. -I/usr/include/jsoncpp -MM ReadJsonCfg.cpp >  ./.depend;
        g++ -g -O0 -std=c++11 -I. -I/usr/include/jsoncpp  -c -o ReadJsonCfg.o ReadJsonCfg.cpp
        g++ ReadJsonCfg.o -L/usr/local/lib -ljsoncpp -o readjsoncfg
        readjsoncfg compilation success!
         > ./readjsoncfg 
        ______ cfg_root : start ______
        {
                "Config" : 
                {
                        "buffer-length" : 5000,
                        "server-ip" : "10.10.10.20",
                        "server-port" : "5555"
                },
                "Note" : "This is a cofiguration file"
        }
        ______ cfg_root : end ________
        ______ Configuration ______
        server-ip     :10.10.10.20
        server-port   :5555
        buffer-length :5000
         > 
        

        【讨论】:

        • 感谢您的回答,您能否分享一下您提供此作为答案的原因,以便为用户提供一些上下文。
        • @Daniel 我已经添加了上下文(“读取 json 配置文件”)。
        【解决方案6】:

        像这样存储人

        {"Anna" : { 
          "age": 18,
          "profession": "student"},
        "Ben" : {
          "age" : "nineteen",
          "profession": "mechanic"}
         }
        

        会引起问题,特别是如果不同的人有相同的名字..

        宁可使用数组存储这样的对象

        {
          "peoples":[
               { 
                   "name":"Anna",  
                   "age": 18,
                   "profession": "student"
               },
               {
                   "name":"Ben",
                   "age" : "nineteen",
                   "profession": "mechanic"
               } 
          ]
        }
        

        像这样,您可以枚举对象,或通过数字索引访问对象。 请记住,json 是存储结构,而不是动态排序器或索引器。 使用存储在 json 中的数据根据​​需要构建索引并访问数据。

        【讨论】:

          【解决方案7】:

          本质上,javascript 和 C++ 遵循两种不同的原则。 Javascript 创建一个“关联数组”或哈希表,它将字符串键(字段名称)与值匹配。 C++在内存中布局结构,所以前4个字节是一个整数,是一个年龄,那么也许我们有一个固定的32字节字符串,代表“职业”。

          因此,javascript 将处理诸如“年龄”在一条记录中为 18 岁和在另一条记录中为“十九岁”之类的事情。 C++ 不能。 (但是 C++ 快得多)。

          因此,如果我们想在 C++ 中处理 JSON,我们必须从头开始构建关联数组。然后我们必须用它们的类型标记值。它是整数、实数值(可能返回为“double”)、布尔值还是字符串?因此,JSON C++ 类是相当大的代码块。实际上,我们正在做的是在 C++ 中实现一些 javascript 引擎。然后我们将 JSON 作为字符串传递给我们的 JSON 解析器,它会对其进行标记,并为我们提供从 C++ 查询 JSON 的函数。

          【讨论】:

          • C++ 有 std::map 用于关联映射,无需从头开始构建它。为了获得更大的灵活性,请使用std::map&lt;std::string, std::variant&lt;bool, int, std::string ...&gt;&gt;,其中 std::variant 涵盖了您在 json 值中可能遇到的类型,并且您确实拥有完整的 JSON 功能。
          【解决方案8】:
          1. 是的,您可以创建一个嵌套数据结构people,它可以被AnnaBen 索引。但是,你不能直接通过ageprofession 来索引它(我将在代码中进入这部分)。

          2. people 的数据类型是Json::Value 类型(在 jsoncpp 中定义)。没错,它类似于嵌套映射,但Value 是一种数据结构,它被定义为可以存储和访问多种类型。它类似于以string 作为键和Json::Value 作为值的映射。它也可以是 unsigned int 作为键和 Json::Value 作为值之间的映射(如果是 json 数组)。

          代码如下:

          #include <json/value.h>
          #include <fstream>
          
          std::ifstream people_file("people.json", std::ifstream::binary);
          people_file >> people;
          
          cout<<people; //This will print the entire json object.
          
          //The following lines will let you access the indexed objects.
          cout<<people["Anna"]; //Prints the value for "Anna"
          cout<<people["ben"]; //Prints the value for "Ben"
          cout<<people["Anna"]["profession"]; //Prints the value corresponding to "profession" in the json for "Anna"
          
          cout<<people["profession"]; //NULL! There is no element with key "profession". Hence a new empty element will be created.
          

          如您所见,您可以仅根据输入数据的层次结构对json对象进行索引。

          【讨论】:

          • 谢谢!我现在明白了这个想法。但是,它还不是很有效:如果我只将json-forwards.hjson.hjsoncpp.cpp 添加到项目中,我会收到一条错误消息“二进制表达式的操作数无效('std::ifstream 和'Json::Value' " 在 people_file >> people 行。如果我在 include/ 中添加所有文件,我会收到一条错误消息 "来自阅读器的错误:* 第 1 行,第 1 列语法错误:需要值、对象或数组。" 标题搜索路径设置为“include/”和“dist/”。
          • 我猜你必须包含json/json.h 而不是json.h。我不完全确定,因为我使用 jsoncpp 已经有一段时间了。我找到了一个更简单的替代方案nlohmann json。它具有几乎相同的功能,并且非常易于使用。您只需包含一个文件#include&lt;json.hpp&gt;。你可能想看看它。
          • 有人声明为json::Value people;吗?声明丢失
          • 给出错误:`json/value.h: No such file or directory`
          • 如果这对任何人都不起作用,请将 #include &lt;json/value.h&gt; 更改为 #include &lt;json/json.h&gt;
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-05-04
          • 2012-10-29
          • 2018-01-07
          • 1970-01-01
          • 2023-03-06
          • 2022-08-19
          • 1970-01-01
          相关资源
          最近更新 更多