55

I'm trying to read in a JSON file. So far I have focused on using the jsoncpp library. However, the documentation is quite hard to understand for me. Could anyone explain in lay terms what it does?

Say I have a people.json which looks like this:

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

What happens when I read this in? Can I create some sort of data structure people which I can index by Anna and Ben as well as age and profession? What would be the data type of people? I thought it would be something similar to a (nested) map, but map values always have to have the same type, don't they?

I have worked with python before and my "goal" (which may be ill-set for C++) is to obtain the equivalent of a nested python dictionary.

mskfisher
  • 3,291
  • 4
  • 35
  • 48
user3515814
  • 957
  • 2
  • 8
  • 13

9 Answers9

46
  1. Yes you can create a nested data structure people which can be indexed by Anna and Ben. However, you can't index it directly by age and profession (I will get to this part in the code).

  2. The data type of people is of type Json::Value (which is defined in jsoncpp). You are right, it is similar to the nested map, but Value is a data structure which is defined such that multiple types can be stored and accessed. It is similar to a map with a string as the key and Json::Value as the value. It could also be a map between an unsigned int as key and Json::Value as the value (In case of json arrays).

Here's the code:

//if include <json/value.h> line fails (latest kernels), try also:
//  #include <jsoncpp/json/json.h>
#include <json/value.h>
#include <fstream>

std::ifstream people_file("people.json", std::ifstream::binary);
Json::Value people;
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.

As you can see, you can index the json object only based on the hierarchy of the input data.

ntg
  • 12,950
  • 7
  • 74
  • 95
Pooja Nilangekar
  • 1,419
  • 13
  • 20
  • Thanks! I understand the idea now. However, it's not quite working yet: If I only add `json-forwards.h`, `json.h` and `jsoncpp.cpp` to the project I get an error message "Invalid operands to binary expression('std::ifstream and 'Json::Value'" at the line people_file >> people. If I do add all the files in `include/` I get an error message "Error from reader: * Line 1, Column 1 Syntax error: value, object or array expected." Header search paths are set to both 'include/' and 'dist/'. – user3515814 Aug 26 '15 at 09:55
  • 2
    I am guessing you'll have to include `json/json.h` instead of `json.h`. I am not entirely sure as it's been a while since I used jsoncpp. I found a much simpler alternative [nlohmann json](https://github.com/nlohmann/json). It has pretty much the same functionality and is very easy to use. You only have to include a single file, `#include`. You might want to have a look at it. – Pooja Nilangekar Aug 26 '15 at 11:08
  • 18
    Is people declared as `json::Value people;`? The declaration is missing – roschach Sep 06 '18 at 13:47
  • 1
    gives error: ` json/value.h: No such file or directory` – Ciasto piekarz Apr 12 '20 at 14:29
  • @PoojaNilangekar It is giving the error that people_file doesn't name a type. – y_159 Aug 09 '20 at 15:41
  • 5
    If this isn't working for anyone, change `#include ` to `#include ` – Penguin Dec 23 '20 at 05:37
  • or if you have installed the library via `apt install libjsoncpp-dev` in newer linux kernels use`#include ` – a-sam Jun 19 '21 at 15:39
31

Have a look at nlohmann's JSON Repository on GitHub. I have found that it is the most convenient way to work with JSON.

It is designed to behave just like an STL container, which makes its usage very intuitive.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Arsen
  • 654
  • 8
  • 20
6

Essentially javascript and C++ work on two different principles. Javascript creates an "associative array" or hash table, which matches a string key, which is the field name, to a value. C++ lays out structures in memory, so the first 4 bytes are an integer, which is an age, then maybe we have a fixed-wth 32 byte string which represents the "profession".

So javascript will handle things like "age" being 18 in one record and "nineteen" in another. C++ can't. (However C++ is much faster).

So if we want to handle JSON in C++, we have to build the associative array from the ground up. Then we have to tag the values with their types. Is it an integer, a real value (probably return as "double"), boolean, a string? It follows that a JSON C++ class is quite a large chunk of code. Effectively what we are doing is implementing a bit of the javascript engine in C++. We then pass our JSON parser the JSON as a string, and it tokenises it, and gives us functions to query the JSON from C++.

Malcolm McLean
  • 6,258
  • 1
  • 17
  • 18
  • 3
    C++ has `std::map` for associative maps, no need to build it from the ground up. For further flexibility, use `std::map>` where the std::variant covers the types you may encounter in the json values, and you literally have full blown JSON capabilities. – Petter Kvalvaag Jul 11 '19 at 10:26
6

complete minimal working example for fedora 33

Get the library with sudo dnf install jsoncpp-devel.

Store the following files in your projects folder:

people.json

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

main.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";
}

Compile with g++ -ljsoncpp main.cpp. Calling ./a.out yields the

output

{
    "age" : 18,
    "profession" : "student"
}
"student"
Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58
4

You can use c++ boost::property_tree::ptree for parsing json data. here is the example for your json data. this would be more easy if you shift name inside each child nodes

#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";
    }  
}
ChauhanTs
  • 439
  • 3
  • 6
3

Example (with complete source code) to read a json configuration file:

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
 > 
3

Here is another easier possibility to read in a json file:

#include "json/json.h"

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

You can then get the values like this:

cout << root["key"]
LaLa
  • 642
  • 2
  • 7
  • 11
1

storing peoples like this

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

will cause problems, particularly if differents peoples have same name..

rather use array storing objects like this

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

like this, you can enumerates objects, or acces objects by numerical index. remember that json is storage structure, not dynamically sorter or indexer. use data stored in json to build indexes as you need and acces data.

0

I used the boost::json library and the following code to get the JSON from the file into a boost::json::value:

#include <iostream>
#include <fstream>
#include <boost/json/src.hpp>

int main() 
{
    std::ifstream input("/tmp/json_data.txt");
    std::stringstream buffer;
    buffer << input.rdbuf();
    boost::json::value json_data = Json::parse(buffer.str());
    std::cout << "json_data:\n" << json_data << std::endl;
    return 0;
}
Will
  • 1,509
  • 1
  • 13
  • 16