7

I want to use jsoncpp for writing a C++ code in order to parse a JSON file. Let me explain what I did. I created a CMakeLists.txt and I made a FindJsoncpp.cmake along with a simple c++ file to test jsoncpp. When I compile the C++ source without cmake using -I/usr/include/jsoncpp/ -ljsoncpp it works fine. but when I try to build it using cmake it cannot find json.h header file that I included in my c++ source code.

here is my CMakeLists.txt:

cmake_minimum_required (VERSION 2.6)
project (Parser)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")

include(LibFindMacros)

message("----------- trying to find Jsoncpp-------------")
find_package(Jsoncpp)

if(Jsoncpp_FOUND)
    message("INFO: we found LibJsoncpp on your pc.")
    message(Jsoncpp_FOUND = ${Jsoncpp_FOUND})
    message(Jsoncpp_INCLUDE_DIR = ${Jsoncpp_INCLUDE_DIR}) 
    message(Jsoncpp_LIBRARY = ${Jsoncpp_LIBRARY})
else(Jsoncpp_FOUND)
    message("WARNING: we couldn't find LibJsoncpp on your pc. DLC is disabled.")
endif(Jsoncpp_FOUND)

#set(LIBS ${Jsoncpp_LIBRARY})

# Set the include dir variables and the libraries and let libfind_process do the rest.
# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
set(Jsoncpp_PROCESS_INCLUDES Jsoncpp_INCLUDE_DIR)
set(Jsoncpp_PROCESS_LIBS Jsoncpp_LIBRARY)

# add the executable
add_executable(jsonparser jsonparser.cpp)

And this is the FindJsoncpp.cmake that I wrote:

# - Try to find Jsoncpp
# Once done, this will define
#
#  Jsoncpp_FOUND - system has Jsoncpp
#  Jsoncpp_INCLUDE_DIRS - the Jsoncpp include directories
#  Jsoncpp_LIBRARIES - link these to use Jsoncpp

include(LibFindMacros)

# Use pkg-config to get hints about paths
libfind_pkg_check_modules(Jsoncpp_PKGCONF jsoncpp)

# Include dir
find_path(Jsoncpp_INCLUDE_DIR
  NAMES json/json.h
#  PATHS ./jsoncpp/
  PATHS ${Jsoncpp_PKGCONF_INCLUDE_DIRS} # /usr/include/jsoncpp/json
)

# Finally the library itself
find_library(Jsoncpp_LIBRARY
  NAMES jsoncpp
  PATHS ${Jsoncpp_PKGCONF_LIBRARY_DIRS}
#  PATH ./jsoncpp/
)

set(Jsoncpp_PROCESS_INCLUDES Jsoncpp_INCLUDE_DIR)
set(Jsoncpp_PROCESS_LIBS Jsoncpp_LIBRARY)
libfind_process(Jsoncpp)

And finally a simple C++ code called jsonparser.cpp to test it:

#include <iostream>
#include <fstream>
#include <json/json.h>
using namespace std;

void printSongInfo(Json::Value song){
    std::clog<<"\n-----------printing a song-------------\n";
    std::clog<<"Name="<<song["name"];
    std::clog<<"Artist="<<song["artist"];
}

int main(){

    std::ifstream catalogFile("catalog.json");

    Json::Value root;   // will contains the root value after parsing.
    Json::Reader reader;
    bool parsingSuccessful = reader.parse( catalogFile, root );
    if ( !parsingSuccessful ){
        // report to the user the failure and their locations in the document.
        std::cout  << "Failed to parse configuration\n"
                   << reader.getFormattedErrorMessages();
        return 1;
    }

    //parsing songs
    const Json::Value songs = root["songs"];
    for ( int index = 0; index < songs.size(); ++index ){  // Iterates over the sequence elements.
       printSongInfo(songs[index] );
    }
    return 0;
}

When I run the jsonparser.cpp with below command it works just fine.

g++ -I/usr/include/jsoncpp/ -ljsoncpp jsonparser.cpp

but when I try to make it using cmake I get this error:

$~/jsoncppTest/build$ cmake ..
-- The C compiler identification is GNU 4.7.3
-- The CXX compiler identification is GNU 4.7.3
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
----------- trying to find Jsoncpp-------------
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.26") 
-- checking for module 'jsoncpp'
--   found jsoncpp, version 0.6.0
-- Found Jsoncpp 
INFO: we found LibJsoncpp on your pc.
Jsoncpp_FOUND=TRUE
Jsoncpp_INCLUDE_DIR=/usr/include/jsoncpp
Jsoncpp_LIBRARY=/usr/lib/libjsoncpp.so
-- Configuring done
-- Generating done
-- Build files have been written to: ~/jsoncppTest/build
$~/jsoncppTest/build$ make
Scanning dependencies of target jsonparser
[100%] Building CXX object CMakeFiles/jsonparser.dir/jsonparser.cpp.o
~/jsoncppTest/jsonparser.cpp:3:23: fatal error: json/json.h: No such file or directory
compilation terminated.
make[2]: *** [CMakeFiles/jsonparser.dir/jsonparser.cpp.o] Error 1
make[1]: *** [CMakeFiles/jsonparser.dir/all] Error 2
make: *** [all] Error 2

It cannot find json/json.h header file but it has founded the jsoncpp library in cmake before. I checked jsoncpp.pc file and found ti OK. I don't know what I am doing wrong! any help would be appreciated.

I am using ubuntu 13.04 with multiarch support. I heard something about jsoncpp problem with 64bit compiler but don't know if that's the case.

sajjadG
  • 2,546
  • 2
  • 30
  • 35
  • @bennofs the error changed but still not working. it gives this error `jsonparser.cpp:(.text+0x29): undefined reference to Json::Value::operator[](char const*) jsonparser.cpp:(.text+0x46): undefined reference to Json::operator<<(std::ostream&, Json::Value const&) j` – sajjadG Aug 01 '13 at 23:26
  • try `target_link_libraries(json_parser $(Jsoncpp_LIBRARIES))`, does that work? (I removed my earlier comment because I thought it was stupid, seems it wasn't that stupid) – bennofs Aug 01 '13 at 23:29
  • @bennofs It says : `Cannot specify link libraries for target "jsonparser" which is not built by this project.` – sajjadG Aug 01 '13 at 23:33
  • @sajjadG Looks like a typo. It should be "json_parser", not "jsonparser". Also, I think @bennofs meant `target_link_libraries(json_parser ${Jsoncpp_LIBRARIES})` (note the curly braces round `Jsoncpp_LIBRARIES`. – Fraser Aug 01 '13 at 23:37
  • I guess the deleted comment was to add `include_directories(${Jsoncpp_INCLUDE_DIRS})` ? – Fraser Aug 01 '13 at 23:39
  • Also, after looking at it, it seems really difficult (if not impossible) to make a FindJsoncpp that works for all systems. This is because there is also json-c, which installs a header /usr/include/json/json.h on my system. But that header might also belong to jsoncpp (the default make install of jsoncpp installs a header in that location). – bennofs Aug 01 '13 at 23:39
  • @Fraser No, it was to add jsoncpp as directory to the include, because jsoncpp is in /usr/include/jsoncpp/json/json.h on my system, and there is also /usr/include/json/json.h, which is not compatible. – bennofs Aug 01 '13 at 23:40
  • From the output, it looks like the find module is doing the right thing. It says `Jsoncpp_INCLUDE_DIR=/usr/include/jsoncpp`. Doing `include_directories(${Jsoncpp_INCLUDE_DIRS})` in the CMakeLists.txt should mean that `#include ` will work properly in the source file. – Fraser Aug 01 '13 at 23:44
  • @bennofs It should be possible to create a cmake module. jsoncpp header is in `/usr/include/jsoncpp/json/json.h` and I don't think any other header goes there. I should say that cmake uses `pkg-config` to find `jsoncpp header` and `libjsoncpp.so` directory with has been detected just fine as @Fraser mentioned it. – sajjadG Aug 02 '13 at 00:02
  • @Fraser `target_link_libraries(json_parser ${Jsoncpp_LIBRARIES})` also gave me the same error `Cannot specify link libraries for target "json_parser" which is not built by this project.` – sajjadG Aug 02 '13 at 00:13
  • @sajjadG - Sorry, I lied :-( The target's name defined in `add_executable` is indeed "jsonparser" and not "json_parser". I think this is probably down to calling `target_link_libraries` before you've defined the target in `add_executable`. The order needs to be `add_executable`, then `target_link_libraries` as per @bennofs' answer. – Fraser Aug 02 '13 at 00:20
  • @Fraser It was not a lie. you simply made a mistake. It happens to everyone. Don't call it a lie. You are a great man ;) – sajjadG Aug 02 '13 at 00:25

2 Answers2

9

Ok, I have a solution that compiles fine on my system. Finding jsoncpp is tricky, because json-c installs a header with the same name, and on my system, that header is located under /usr/include/json/json.h. To get it work, you have to make the following changes:


in FindJsoncpp.cmake:

# Include dir
find_path(Jsoncpp_INCLUDE_DIR
  NAMES json/features.h
  PATH_SUFFIXES jsoncpp
  PATHS ${Jsoncpp_PKGCONF_INCLUDE_DIRS} # /usr/include/jsoncpp/json
)

Searching for json/features.h instead of json/json.h avoids finding the json.h file of json-c on my system, which is not compatible.


in CMakeLists.txt :

include_directories(${Jsoncpp_INCLUDE_DIR})
add_executable(jsonparser jsonparser.cpp)
target_link_libraries(jsonparser ${Jsoncpp_LIBRARY})

Here the found directories are set up, so CMake actually uses them.


in jsonparser.cpp:

const Json::Value songs = root["songs"];
for ( int index = 0; index < songs.size(); ++index ){  // Iterates over the sequence elements.
   std::clog<<"Name="<<songs[index]["name"];
   std::clog<<"Artist="<<songs[index]["artist"];
}

Your orginal code didn't compile, so I replaced the offending piece with the code above. Have you forgot to declare the song variable?


I also removed the getFormattedErrorMessages() call, because I have only jsoncpp 0.5.0, in which that function is not available. That shouldn't make a difference though.

Let me know if this works for you.

bennofs
  • 11,873
  • 1
  • 38
  • 62
  • Good answer... +1. I thought more about your comment regarding the 2 different json.h files, and I wonder if the most robust solution would be to do `find_path(... jsoncpp/json/json.h ...)` and then in the source files you'd have to do `#include "jsoncpp/json/json.h"`. No chance of accidentally picking up the wrong json.h then. – Fraser Aug 02 '13 at 00:15
  • @Fraser I thought about that too, but unfortunaly, the default install (make install in the source code) *does not* create the jsoncpp directory, but would install into /usr/include/json/. So there might be systems where the jsoncpp's json.h is in that directory. – bennofs Aug 02 '13 at 00:17
  • Ah, right. That's a pity, since I suppose there's a pretty good chance `/usr/include` will be included too via some other dependency? – Fraser Aug 02 '13 at 00:22
  • @Fraser Yes, this code depends on the fact that user defined include paths get searched before the system search paths. [GCC does this](http://gcc.gnu.org/onlinedocs/cpp/Search-Path.html), for other compilers, I don't know. – bennofs Aug 02 '13 at 00:33
  • @bennofs It worked :) Thanks a lot. I spent 2 days solving it but you help me in in an hour. I don't know how to thanks you. And about the source code. I had it updated. I must change it badly after copying. anyway the source is ok now. I will be thankful if you can discuss more about the `CMakeLists.txt` part. I think I will encounter this kink of problem later with other libraries. then it would be nice to learn cmake concept better. – sajjadG Aug 02 '13 at 00:40
  • @bennofs I did some investigation. I was curois to see which parts was the cause of my problem. The `FindJsoncpp.cmake` or the `CMakeLists.txt`. I changed the `FindJsoncpp.cmake` Module to before and build it again. It worked. so the problem was with CMakeLists.txt. I don't have `json-c` installed on my system. so not a good test. – sajjadG Aug 02 '13 at 00:42
3

jsoncpp now builds with cmake.

cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -G "Unix Makefiles" ../..
  • supports pkg-config
  • builds static lib, dynamic lib, or both
  • can be included into other projects

If you have suggestions, open an issue at GitHub.

cdunn2001
  • 17,657
  • 8
  • 55
  • 45