简介:这是一份已编译好的 Jsoncpp 库,面向使用 Visual Studio 进行 Windows 桌面开发的 C++ 程序员,适合需要 JSON 解析、序列化与配置读取的中小型工具及客户端项目。库按 C++ 方式编译导出,未按 C 接口封装,能直接使用 Json::Value 等对象模型,项目以 C++ 为主时接入更自然;同时提供动态库与静态库两种形态,开发者可根据部署方式灵活选择。压缩包共 6 个文件,包含 2 个头文件、2 个 lib 文件、2 个 dll 文件,对应普通版与调试版(_d)构建,整体仅 892KB,轻量易集成;普通版用于 Release 构建,调试版便于断点排错。已有 520 人学习下载。拿到后无需再自行编译源码,也省去处理 CMake 配置、导出宏与运行库选项的麻烦;头文件与导入库、动态库按 include/lib 分离存放,目录直观;在工程中引用头文件并链接对应 lib 即可使用 Json::Value 完成 JSON 对象读写、数组遍历、错误处理等常见操作,也适合日志结构化、接口联调和配置持久化等开发任务。
1. 当 C++ 项目突然缺了 json 解析:Jsoncpp 动态库与静态库的取舍之道
接手过一个用 C++ 写的跨平台数据采集工具,代码逻辑不复杂,但一到部署阶段就头疼——目标机器上没装 Jsoncpp 的 runtime,程序一启动就报error while loading shared libraries: libjsoncpp.so。后来我把链接方式从动态改成静态,把库直接编进可执行文件里,问题才彻底消失。这就是 C++ Jsoncpp 库的动态与静态之争:动态库省空间、更新方便,但部署时要保证环境里有对应版本的.so或.dll;静态库把所有东西打进可执行文件,换来的是省心,代价是体积变大。这篇笔记会从编译、链接、参数设置到踩坑记录,把 Jsoncpp 动态库和静态库的完整流程拆开给你看,适合刚接触 CMake 和 C++ 依赖管理的初学者,也适合被部署环境折腾过的熟手对照排查。
2. 动态库与静态库:先搞懂两者本质差异,再谈选型
2.1 编译期的谎言:链接时你看到的和运行时完全是两回事
很多新手第一次编译 Jsoncpp 时会有个错觉:链接器告诉我说libjsoncpp.a或libjsoncpp.so都在这,随便用一个不就行了?这里面藏着一个经典误解。我用一个最小例子来说明:
// main.cpp #include <json/json.h> #include <iostream> int main() { Json::Value root; root["name"] = "test"; std::cout << root.toStyledString() << std::endl; return 0; }如果用静态库编译,命令是这样的:
g++ main.cpp -o app_static -I./jsoncpp/include -L./jsoncpp/lib -ljsoncpp如果是动态库,命令几乎一模一样:
g++ main.cpp -o app_dynamic -I./jsoncpp/include -L./jsoncpp/lib -ljsoncpp问题就出在这——编译期链接器不区分你用的是静态库还是动态库。默认情况下,如果libjsoncpp.so和libjsoncpp.a同时存在,GCC 会优先选.so。真正区别在运行时:app_static不依赖任何外部.so文件,直接跑;app_dynamic需要系统里能找到libjsoncpp.so,否则就会报错。
这和 Jsoncpp 本身无关,是所有 C/C++ 项目都会遇到的基础机制。选型的核心逻辑是:如果你控制部署环境,动态库更省事;如果你要把程序发给别人或放到未知环境,静态库更稳。
2.2 分别什么时候用动态、什么时候用静态,我给三条判断标准
我在实际项目里通常这样判断:
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 自己维护的服务器,能控制依赖版本 | 动态库 | 升级 Jsoncpp 只需替换.so,不用重新编译主程序 |
| 发给外部客户或部署到未知环境 | 静态库 | 避免对方机器上没有 Jsoncpp 运行时的尴尬 |
| 追求可执行文件体积最小化 | 动态库 | 静态链接会让二进制体积多出几百 KB 到几 MB |
还有一条中间路线:动态库加载失败后降级到静态库。我见过有的项目是把两个库都带上,运行时先用dlopen尝试加载动态库,失败就 fallback 到一个静态链接的备用可执行文件。但这种方案维护成本高,除非是特别在意兼容性的场景,否则我一般不建议折腾。
另一个真实的经验是:如果项目里只有 Jsoncpp 一个第三方库,直接用静态库最省心。如果项目里已经用了五六个动态库,那 Jsoncpp 也老老实实编成动态就好,统一管理方式比单点最优更重要。
2.3 版本与构建系统:不同 Jsoncpp 版本的库文件结构差异
Jsoncpp 的构建脚本历经了几次变化。老版本(0.x 系列)用scons构建,新版本(1.x 系列)主推 CMake。如果你是从 GitHub 直接拉代码编译,注意分支是master还是0.y.z分支,两者的产物结构不一样:
- 1.x 版通过 CMake 构建后,静态库和动态库都在
lib/目录下,头文件在include/下 - 0.x 版用
scons时,产出文件可能在build/或libs/这个目录里,且容易受 Python 版本影响
所以下载源码包后,最先做的事情是看清楚它的构建说明,别急着跑命令。我踩过一次坑:对 0.x 版本用 CMake 去编,结果生成了一堆头文件缺失的错误——后来才发现这版本根本没写 CMakeLists.txt。如果你用的是较新的发行版,直接走 CMake 就好。
3. 从源码编译 Jsoncpp 动态库与静态库:完整流程与关键参数
3.1 依赖准备:你只需要一个 C++ 编译器和一个 CMake 环境
Jsoncpp 的源码编译依赖非常干净,不像其他库动不动给你拉一堆第三方库进来。这是它受欢迎的一个重要原因——绝赞的零依赖设计。你只需要:
- GCC 或 Clang,版本至少支持 C++11(Jsoncpp 1.x 默认用 C++11 标准)
- CMake 3.1 以上
- Python(可选,用于运行测试脚本,不用的话测试可以跳过)
# Ubuntu/Debian 环境安装依赖 sudo apt-get install g++ cmake # CentOS/RHEL 环境 sudo yum install gcc-c++ cmake # macOS brew install gcc cmakeWindows 上用 Visual Studio 的开发者命令行工具即可,CMake 会自动识别 MSVC 编译器。我自己在 Windows 上用 CMake + Visual Studio 构建过 Jsoncpp,过程没什么特殊的,只是最后生成的库文件名可能是jsoncpp.lib(静态)和jsoncpp.dll(动态),命名风格不同但不影响使用。
3.2 两种构建方式:一行命令与自定义参数
我把两种构建方式都跑过,最简单的模式是:
git clone https://github.com/open-source-parsers/jsoncpp.git cd jsoncpp mkdir -p build cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j4运行完你会发现build/lib/下面既生成了libjsoncpp.a,也生成了libjsoncpp.so(macOS 上是.dylib)。这是因为 Jsoncpp 的 CMakeLists.txt 默认同时构建静态库和动态库。如果你只想构建某一种,可以这样指定:
# 只构建静态库 cmake .. -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON # 只构建动态库 cmake .. -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF-DCMAKE_BUILD_TYPE=Release这个参数容易被忽略,但很重要——没加的时候默认是空类型,编译器优化级别和调试信息都是默认状态,性能会差不少。Debug 版本生成的库体积更大,链接进去的程序跑起来也慢一些,发布时一定要切到 Release。
3.3 符号可见性:隐藏内部符号能让动态库更“干净”
这是动手编译 Jsoncpp 时一个值得注意的细节。Jsoncpp 库内部有很多辅助类,默认情况下编译动态库时,所有非静态符号都会导出到动态符号表里。这样在运行时解析符号时,会有更大概率发生符号冲突——比如别的一个库也定义了同名函数,动态链接器可能会用错符号。
CMake 参数可以控制这个行为:
cmake .. -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_VISIBILITY_INLINES_HIDDEN=ON设置后,只有标记了JSONCPP_API的符号才会被导出,其他内部符号全部隐藏。这样做有两个实际好处:
- 动态库体积会小一些(符号表缩小了)
- 减少与其他库的符号冲突概率
代价是调试时看不到内部符号,对排查问题有轻微影响。如果你只是临时用用,不加这个参数没毛病;要是准备把 Jsoncpp 作为系统库安装,建议加上。
3.4 安装到指定前缀:别让头文件和库散落在系统目录
编译完成后,默认情况下make install会装到/usr/local下,包含/usr/local/include/json/和/usr/local/lib/libjsoncpp.*。如果不想污染系统目录,可以用-DCMAKE_INSTALL_PREFIX指定自己的安装位置:
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/third_party/jsoncpp make -j4 make install不指定前缀直接make install,会往系统目录写文件。有的发行版包管理器对/usr/local目录是宽容的,但有些系统(比如某些严格配置的 CentOS)会禁止往/usr/local/lib写入。在日常开发中,把第三方库集中在前缀third_party/让链接路径可控,比散落到系统目录更好管理。
4. 在自己的项目里链接 Jsoncpp:CMake 与命令行两种方式
4.1 最常见的翻车现场:CMake 里找不到 Jsoncpp
很多人在 CMake 里接 Jsoncpp 时,第一个念头是find_package(jsoncpp)。然而 Jsoncpp 不提供标准的 CMake config 文件,大多数老版本直接找不到。你会看到这类报错:
CMake Error: Could not find a package configuration file provided by "jsoncpp"这时的标准做法是改用pkg-config机制。Jsoncpp 在安装时会生成jsoncpp.pc文件,你可以用:
find_package(PkgConfig REQUIRED) pkg_check_modules(JSONCPP REQUIRED jsoncpp)但 pkg-config 本身也依赖环境变量PKG_CONFIG_PATH指向正确位置。如果 Jsoncpp 装到了自定义前缀下,不设置这个变量依然找不到。与其纠结find_package和pkg-config的路径问题,我更喜欢直接在 CMake 里用绝对路径指定:
4.2 用 target_link_libraries 精确指定动态/静态库
cmake_minimum_required(VERSION 3.10) project(json_demo) set(JSONCPP_ROOT "$ENV{HOME}/third_party/jsoncpp") include_directories(${JSONCPP_ROOT}/include) add_executable(json_demo main.cpp) # 使用动态库 target_link_libraries(json_demo ${JSONCPP_ROOT}/lib/libjsoncpp.so) # 使用静态库(两种写法任选) # target_link_libraries(json_demo ${JSONCPP_ROOT}/lib/libjsoncpp.a)关键点说明:
$ENV{HOME}是 CMake 读取环境变量的语法,确保路径可移植- 用
.a还是.so后缀直接决定最终链接方式,不用再额外配置 - 动态库在 Windows 上需要链接导入库
.lib,运行时要放.dll;静态库直接链接.lib即可
但这里有一个坑:如果同时存在.so和.a,并且你用的是-ljsoncpp,CMake 默认也会优先选.so。想强制用静态库,可以在 CMake 里设置:
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)这条命令把查找后缀锁定为.a,CMake 就不会去搜.so了。注意要在find_library或target_link_libraries之前设置才生效。
4.3 纯命令行编译的完整示例
不依赖 CMake 时,我用命令行直接编译,把四个关键参数写清楚:
# 动态库链接 g++ main.cpp -o app_dynamic \ -I$HOME/third_party/jsoncpp/include \ -L$HOME/third_party/jsoncpp/lib \ -ljsoncpp \ -Wl,-rpath,$HOME/third_party/jsoncpp/lib # 静态库链接 g++ main.cpp -o app_static \ -I$HOME/third_party/jsoncpp/include \ $HOME/third_party/jsoncpp/lib/libjsoncpp.a参数说明:
-I:指定头文件搜索目录,让#include <json/json.h>能找到头文件-L:指定库文件搜索目录,配合-ljsoncpp使用-Wl,-rpath:把动态库搜索路径写进可执行文件里,运行时不用设置LD_LIBRARY_PATH,但只对 GNU 链接器有效- 静态链接直接写
.a的完整路径,绕开-L和-l的搜索逻辑
在实际操作时,-Wl,-rpath这个参数很容易漏掉。漏掉后的表现是:编译通过,运行时直接报错找不到.so,人在现场最容易懵的就是这个环节。
4.4 运行时找不到动态库的排查
如果程序编译成功后运行报错:
error while loading shared libraries: libjsoncpp.so: cannot open shared object file: No such file or directory原因要么是动态库不在系统库搜索范围内,要么是你安装了新版本,但程序还链接着旧版本的库。先确认程序实际链接了哪个库:
ldd app_dynamic | grep jsoncpp输出会显示实际加载的库路径。如果路径不对,检查下面的设置:
# 临时设置库搜索路径 export LD_LIBRARY_PATH=$HOME/third_party/jsoncpp/lib:$LD_LIBRARY_PATH # 或者把库安装到系统搜索目录 sudo cp $HOME/third_party/jsoncpp/lib/libjsoncpp.so /usr/local/lib/ sudo ldconfigldd是排查动态库问题时最高频的工具,建议每次遇到此类报错先跑一次它,能省很多盲目翻找的时间。
5. 避坑手册:Jsoncpp 库编译链接的五个经典坑
5.1 动态库与静态库同时存在,链接器悄悄选了动态库
现象:你明明想让程序链接静态库,也把.a的路径写在了-l参数的目录里,但程序运行时仍然报找不到.so。
原因:GCC 在同时看到libjsoncpp.so和libjsoncpp.a时,默认优先选.so。你虽然给的是路径目录,但链接器按文件优先级选择,不会因为目录名就自动区分。
解决:最直接的办法是编译时把静态库完整路径直接写在命令中:g++ main.cpp $HOME/third_party/jsoncpp/lib/libjsoncpp.a。这样链接器不会再走搜索逻辑,强制链接这个文件。用 CMake 的场景,就用set(CMAKE_FIND_LIBRARY_SUFFIXES .a)锁定后缀。
5.2 JSONCPP_API 宏导致编译报错:undefined reference
现象:项目编译时一切正常,但链接阶段大量报undefined reference to Json::Value::Value(...)一类错误。
原因:头文件里的JSONCPP_API宏在编译客户端代码时被展开成__attribute__((visibility("default"))),但你的库在编译时设置了-fvisibility=hidden,导出的符号没有包含这些 API,导致客户端找不到符号。
解决:如果用了隐藏可见性参数,在客户端代码里确保定义JSONCPP_DLL(Windows)或JSONCPP_API宏匹配展开。更稳妥的做法:编译 Jsoncpp 时单独用一个构建配置,关闭隐藏符号;或者检查你的编译命令里是否多加了-DJSONCPP_NO_EXPORT之类的定义。在链接前用nm -C libjsoncpp.so | grep "Json::Value"检查符号是否存在,能快速定位问题。
5.3 混合使用不同编译选项导致 ABI 不兼容
现象:程序在本地运行正常,拷到另一台机器上突然崩溃,而且崩溃点在 Jsoncpp 内部。
原因:两台机器上 Jsoncpp 的编译选项不一致。特别常见的是_GLIBCXX_USE_CXX11_ABI这个宏——GCC 5 之后新旧 ABI 切换的标记位。在使用较新 GCC 的系统上编译出来的库,放到老系统上可能不兼容。
解决:发布程序时,要么把依赖的动态库一起带上,要么直接全部静态链接。遇到已经编译好的库出现 ABI 问题时,从源码重新编译一遍通常能解决。用静态库发布时,注意把-D_GLIBCXX_USE_CXX11_ABI=0或=1的配置统一好,在命令行和 CMake 里保持一致。
5.4 Windows 下 DLL 运行时崩溃:Release/Debug 混淆
现象:Windows 上程序运行时崩溃或内存错误,调试时发现 Jsoncpp 的函数调用出现了问题。
原因:Visual Studio 生成的 Debug 版本的库依赖 Debug 运行时,Release 版本依赖 Release 运行时。把 Debug 版本的jsoncpp.lib混入 Release 构建的程序,运行时分配的内存和释放的内存不匹配,触发堆错误。
解决:Windows 上编译 Jsoncpp 时,保证库的配置和你项目配置一致:项目是 Release 就链接 Release 库,Debug 就链接 Debug 库。检查库是哪个版本:用 Visual Studio 的dumpbin /headers jsoncpp.dll查看,能输出 DLL 的编译环境和选项信息。
5.5 pkg-config 找不到 jsoncpp.pc
现象:CMake 里pkg_check_modules(JSONCPP REQUIRED jsoncpp)报错,找不到 jsoncpp 的模块文件。
原因:jsoncpp.pc文件在安装时没有出现在 pkg-config 默认搜索范围内。默认搜索/usr/lib/pkgconfig和/usr/share/pkgconfig,如果你用CMAKE_INSTALL_PREFIX指定了其他位置,就不会被自动搜索。
解决:临时设置环境变量PKG_CONFIG_PATH=$HOME/third_party/jsoncpp/lib/pkgconfig,或者把.pc文件软链接到/usr/lib/pkgconfig下。之后在 CMake 里重新执行构建,问题就解决了。
6. 进阶技巧:一键检测当前链接方式的验证方法
动手写点有实操性的收尾技巧。确认你最终链接的到底是动态库还是静态库,用下面的命令一眼识别:
# 查看可执行文件依赖的库 ldd app_dynamic # 查看可执行文件的动态符号表 readelf -d app_dynamic | grep NEEDED如果NEEDED里有libjsoncpp.so,说明是动态链接;没有,且ldd也没输出 jsoncpp,则是静态链接。我建议在构建脚本里强制加一段验证逻辑:
#!/bin/bash # 构建完成后自动验证链接方式 if ldd ./app_dynamic | grep -q "jsoncpp"; then echo "[验证] app_dynamic 使用动态链接" else echo "[验证] app_dynamic 未链接动态 jsoncpp" fi这类验证脚本在我这已经成了一个习惯——每次构建完,我会强制跑一次ldd确认链接方式正确再进入部署环节,从那以后我排查链接问题的用时明显缩短了。希望这篇 Jsoncpp 动态库与静态库的拆解笔记能帮你少走几步弯路。
本文还有配套的精品资源,点击获取