
简介这是一份已编译好的 jsoncpp 库压缩包面向需要在 Visual Studio 环境下快速集成 JSON 处理能力的 C 开发者免去源码编译与依赖配置的繁琐过程。包体采用标准分发布局include 目录内含 8 个头文件覆盖 Json::Value、Json::Reader、Json::Writer 等核心类支持整数、浮点数、字符串、数组与对象等多种数据类型并提供查询、修改与序列化接口lib 目录包含 2 个编译好的库文件区分 debug/release 构建便于匹配不同工程配置。全包共 10 个文件压缩后仅 1023KB非常轻量。已有 333 人学习/下载适合中高级 C 开发者用于 Web 服务通信、配置文件解析、数据交换与序列化等场景。拿到资源后在 VS 工程中设置好包含目录与依赖项即可直接调用 jsoncpp 的解析、生成及错误处理 API节省搭建时间并降低集成门槛可显著提高 JSON 数据的开发效率无论是快速原型还是正式项目都能快速上手。 最近在整理跨平台项目的时候又碰到jsoncpp的依赖问题。C 项目里解析 JSONjsoncpp 是很多人的默认选择它稳定、接口简单、文档也不少。但真正动起手来你会发现“用起来”和“能链接进去”是两码事。多数情况下我们需要的不是源码而是一份已经编译好的库文件——也就是“已编译的 jsoncpp”。我这次把 Linux、Windows、Android 三个平台的 jsoncpp 都从源码编译了一遍中间踩了不少坑也把编译产物整理成了一套可复用的依赖目录。这篇博文就完整记录我的选型思路、CMake 参数、实测命令以及编译和链接过程中遇到的典型问题定位过程。如果你正在找一份能直接用的 jsoncpp 库或者自己编译时被各种报错卡住这篇应该能帮你省下不少时间。1. 为什么我选择从源码编译 jsoncpp而不是直接挂包管理器1.1 jsoncpp 是一个需要“参与构建”的库jsoncpp 不是 header-only 库它由src/lib_json/下的json_reader.cpp、json_writer.cpp、json_value.cpp几个翻译单元组成编译后生成静态库或者动态库。这意味着你没得选要么用一个别人编好的二进制要么在自己的工程里把它一起编进去。最常用的 API 几乎都围绕Json::Value展开读配置、解析网络协议、生成请求报文、处理测试数据这些都是 jsoncpp 的典型场景。它不像 RapidJSON 那样强调极致性能但胜在稳定、成熟错误信息也比较友好尤其适合业务代码里那种“需要把 JSON 当普通对象用”的写法人。1.2 系统包管理器方案在什么情况下很别扭很多人第一反应是“apt 装一个不就好了”。确实Ubuntu 上执行apt install libjsoncpp-devCentOS 上yum install jsoncpp-devel能很快拿到系统版本的库。如果你的目标平台就是本机而且对版本没有特殊要求直接用系统包是效率最高的方案我完全不反对。但一旦遇到下面这几种情况系统包就有点“不好使”了你要做交叉编译比如用 NDK 编 Android 库或者编 ARM 嵌入式平台系统的 apt 包根本用不上你需要的是静态库而且必须和主工程使用完全一致的编译器版本、工具链参数系统包给的往往是动态库你要裁剪或定制功能比如关掉异常、减小体积、开启特定的宏定义构建服务器比较老旧系统包的版本太旧不符合你代码里StreamWriterBuilder等接口的最低版本要求。这时候自己编译一份“已编译的 jsoncpp”就成了刚需。1.3 自制“预编译包”的核心价值自己编译不光是“把它编出来”更重要的是锁定版本和参数。拿我这次的经验来说三个平台全用 jsoncpp 1.9.x 的同一个 tag编出来的头文件一致接口行为一致就不会出现“Windows 上能编过、Linux 上报 ABI 错误”这种跨平台问题。另外把编译结果固定到一个独立目录里再用 CMake 的find_package或手动 include/lib 方式接到项目中后续无论是换机器、进 CI还是给同事复用都只需要拷贝这个目录不用重新拉网络、不用重新等编译。这才是“已编译”这个词的真正价值它是可以被直接消费的产物而不是一堆散落的源码。2. 正式编译前这几个 CMake 选项一定要先弄清楚2.1 源码获取与版本选择jsoncpp 官方仓库是open-source-parsers/jsoncpp。我没有直接用 master 分支而是选了 1.9.x 的一个稳定 tag。原因很简单master 分支偶尔会有格式变化或新特性引入不稳定而 tag 版本之间接口相对稳定网上能搜到的问题也更多踩坑了容易找到同类反馈。获取源码可以用 gitgit clone --branch 1.9.6 --depth 1 https://github.com/open-source-parsers/jsoncpp.git如果你在内网环境不方便拉 GitHub直接下载对应 tag 的 tar.gz 压缩包也一样。重点是在整个编译过程中所有平台都用同一个版本别 Linux 用 1.9.6、Windows 用 1.7.7那样后面排错会非常痛苦。2.2 jsoncpp 的 CMake 开关对照jsoncpp 从 1.8 之后已经统一用 CMake 作为主构建方式这一点比很多老牌 C 库要省心。打开它的CMakeLists.txt就能看到一大堆可配置项真正决定构建结果的核心参数其实就这几个参数推荐值作用BUILD_SHARED_LIBSOFF控制生成静态库还是动态库。置为 OFF 生成.a/.lib静态库置为 ON 生成.so/.dllJSONCPP_WITH_TESTSOFF不编译单元测试节省构建时间JSONCPP_WITH_CMAKE_PACKAGEON生成 CMake package 配置文件方便后续find_package(JsonCpp)JSONCPP_WITH_PKGCONFIG_SUPPORTON(Linux)生成 pkg-config 文件Linux 下手工编译时很有用CMAKE_BUILD_TYPERelease编译优化等级建议至少RelWithDebInfoCMAKE_INSTALL_PREFIX自定义路径决定安装目录方便统一收集产物其中JSONCPP_WITH_CMAKE_PACKAGE我建议所有平台都打开它生成的JsonCppConfig.cmake文件能让你在业务项目里直接用find_package(JsonCpp CONFIG REQUIRED) target_link_libraries(your_target PRIVATE jsoncpp_static)而不是手工去猜头文件和库文件路径。这个选项很多人会忽略但实际用起来非常香。2.3 容易被忽略的导出宏 JSONCPP_DLL编译 jsoncpp 时有个宏叫JSONCPP_DLL这个宏要特别说明一下。它是 Windows 上__declspec(dllexport)/__declspec(dllimport)的控制开关如果你构建的是静态库且业务代码也是静态链接那么不要定义JSONCPP_DLL如果你构建的是 DLL并在业务代码中链接这个 DLL那么业务代码编译时通常需要定义JSONCPP_DLL。这个宏错配是 Windows 平台下最常见的坑之一现象就是编译一堆“无法解析的外部符号”或者反过来出现一堆__declspec(dllimport)冲突的警告。静态库方案下一般不需要关心它所以这也是我在多数场景下默认选静态库的原因之一。3. 三套实测编译流程Linux、Windows、Android NDK3.1 Linux 平台的编译命令Linux 平台是最顺的只要保证g、cmake、ninja这些基础工具齐全即可。我用的是静态库方便后续整体嵌入业务进程cmake -S . -B build-linux \ -DCMAKE_BUILD_TYPERelease \ -DBUILD_SHARED_LIBSOFF \ -DJSONCPP_WITH_TESTSOFF \ -DJSONCPP_WITH_CMAKE_PACKAGEON \ -DCMAKE_INSTALL_PREFIX$PWD/install-linux cmake --build build-linux -j$(nproc) cmake --install build-linux跑完之后install-linux/lib下面会出现libjsoncpp.ainstall-linux/include下是json/头文件目录。如果你之后要把它压成一个压缩包给别人用直接把整个 install 目录打包即可。在 Linux 下我一般不会手动指定编译器默认的gcc就够了。但如果你的开发机上同时有多个版本的 gcc建议在业务工程里保持“谁编译 jsoncpp 就用谁编译业务代码”的原则避免不同版本 libstdc 混用带来的 ABI 问题。3.2 Windows Visual Studio 的编译细节Windows 下构建 jsoncpp很多人会在“用什么工具链”上犯难。我用的是 Visual Studio 自带的 MSVC 编译器CMake 会自动识别不需要额外装 MinGW。注意 Windows 下不要用CMAKE_BUILD_TYPE因为 VS 的多配置生成器(Ninja Multi-Config 除外)是按--config来选构建类型的cmake -S . -B build-win \ -G Visual Studio 17 2022 -A x64 \ -DBUILD_SHARED_LIBSOFF \ -DJSONCPP_WITH_TESTSOFF \ -DJSONCPP_WITH_CMAKE_PACKAGEON \ -DCMAKE_INSTALL_PREFIX%CD%\install-win cmake --build build-win --config Release cmake --install build-win --config Release这里最容易出错的一点是 CRT 运行时库。MSVC 的 Release 默认使用/MDDebug 默认使用/MDd。如果你把 jsoncpp 的 Release 静态库链接到一个_DEBUG定义的业务工程大概率会在运行期或编译期遇到分配崩溃、_ITERATOR_DEBUG_LEVELmismatch 之类的问题。我的经验是要么业务工程也用 Release要么干脆再编一份 Debug 版 jsoncpp别交叉混用。3.3 Android NDK 交叉编译交叉编译 jsoncpp 给 Android 使用时核心是必须借用 NDK 提供的 CMake toolchain 文件。有了它编译器和 sysroot 会自动配置好不用你去手写复杂的 flags。export ANDROID_NDK/opt/android-ndk-r26b cmake -S . -B build-android \ -DCMAKE_TOOLCHAIN_FILE$ANDROID_NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABIarm64-v8a \ -DANDROID_PLATFORMandroid-21 \ -DBUILD_SHARED_LIBSOFF \ -DJSONCPP_WITH_TESTSOFF \ -DJSONCPP_WITH_CMAKE_PACKAGEON \ -DCMAKE_INSTALL_PREFIX$PWD/install-android-arm64 cmake --build build-android -j$(nproc) cmake --install build-android我编完 arm64-v8a 后会自动复制一份构建目录再改-DANDROID_ABIarmeabi-v7a重编一次以便覆盖主流 Android 设备。值得注意的是ANDROID_PLATFORM不要设置得太高android-21已经能覆盖绝大多数设备设太高反而会让低版本系统无法加载。编译完成后可以检查一下生成物file install-android-arm64/lib/libjsoncpp.a正常会输出ARM aarch64或类似的信息说明交叉编译身份正确没有编成 x86 的库。3.4 快速验证编译结果是否可用编译安装之后不建议直接拿去做业务而是先写一个最小例子验证库文件能不能用。我在每个平台都写了同一个测试程序保证编译、链接、运行链路是通的。#include iostream #include sstream #include json/json.h int main() { std::string text R({msg:ok,code:0}); Json::CharReaderBuilder builder; Json::Value root; std::string errs; std::istringstream stream(text); if (!Json::parseFromStream(builder, stream, root, errs)) { std::cerr parse error: errs std::endl; return 1; } std::cout root[msg].asString() std::endl; return 0; }Linux 下手工编译链接g -stdc11 test.cpp -I install-linux/include -L install-linux/lib -ljsoncpp -o test_jsoncpp这里有一点要提醒-ljsoncpp要放在源文件后面。如果你把它放前面老牌ld会遇到“未定义引用”但找不到库的神奇问题。这也是新手最容易踩的链接顺序坑。4. 把“已编译的 jsoncpp”整理成一份可复用依赖目录4.1 我常用的目录结构模板多个平台编译完成后我习惯把它们统一成下面这样的目录树jsoncpp-1.9.6/ ├── linux-x64/ │ ├── include/json/... │ ├── lib/libjsoncpp.a │ └── lib/cmake/... ├── win-x64/ │ ├── include/json/... │ ├── lib/jsoncpp_static.lib │ └── lib/cmake/... └── android-arm64/ ├── include/json/... └── lib/libjsoncpp.a在最终的工程里CMake 通过CMAKE_PREFIX_PATH指定到对应平台目录后执行find_package(JsonCpp CONFIG REQUIRED)就能正确找到库。如果你不用 CMake也可以手动把include目录添加到头文件搜索路径把lib目录添加到链接搜索路径效果一样。4.2 头文件与库文件必须版本对齐我在实际集成中发现最常见的“已编译库不可用”原因并不一定是编译本身出错而是头文件版本和库文件版本对不上。比如有人在系统目录里有一个旧 jsoncpp 头文件又在工程目录里放进了一份新编译的库结果链接时出现Json::Value的iterator相关报错或者ValueType枚举对不上。所以整理依赖目录时一定要把头文件、导入库、动态库放在同一个安装前缀下不要做“头文件从 A 拿、库文件从 B 拿”这种操作。jsoncpp 说到底是一个 ABI 敏感的库版本的含混会导致一堆莫名其妙的问题。4.3 接入 CMake 工程的两种主流方式如果你的业务工程也是 CMake最推荐的是使用安装时生成的包配置set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party/jsoncpp-1.9.6/linux-x64) find_package(JsonCpp CONFIG REQUIRED) target_link_libraries(your_target PRIVATE jsoncpp_static) target_include_directories(your_target PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/jsoncpp-1.9.6/linux-x64/include)find_package(JsonCpp)成功之后链接目标名到底是jsoncpp_static还是jsoncpp_lib取决于你编译时设置的BUILD_SHARED_LIBS。静态库对应jsoncpp_static动态库对应jsoncpp_lib。这一点容易搞混我特意查过它安装目录下的JsonCppConfig.cmake才确认。如果你不想用安装目录还有一种简单粗暴的方式是直接把src/lib_json/*.cpp拉进你自己的工程一起编译。这种方式省去了生成库文件的麻烦但代价是每次都要编译这几份源码而且后续升级版本时有改错文件的风险。我更推荐已经编译好的静态库方案。4.4 静态库和动态库的选择经验抛开性能因素我在多数业务项目里会优先选静态库原因主要是省心不需要处理运行时找不到 DLL 的问题也不会出现“测试机能跑、生产机缺个.so”的尴尬。动态库的优势是可以在多个可执行文件之间共享同一份内存副本适合插件机制或超大工程场景但随之而来的版本冲突也会多很多。另外一个考虑点是编译选项匹配。如果你在主工程里关闭了 RTTI 或异常而 jsoncpp 是用默认参数编译的静态链接后会埋下隐患动态链接时因为接口边界更加清楚反而没那么明显。如果需要编一个“无异常”版本可以在编译 jsoncpp 时额外加上-DCMAKE_CXX_FLAGS-fno-exceptions -DJSONCPP_EXCEPTION_DISABLEJSONCPP_EXCEPTION_DISABLE是 jsoncpp 给出的一个开关定义后它会用错误码返回代替抛出异常适合嵌入式精简环境。但代码行为会变原本operator[]抛出异常的地方现在可能返回一个无效 Value逻辑上要重新检查。5. 编译和链接期最常踩的坑附带完整定位链路5.1 Windows 下 LNK2019到底是实现缺失还是宏错配我在 Windows 下遇到过不止一次LNK2019 无法解析的外部符号第一反应是“库没链接进去”。但实际上真正原因往往有三种链接库路径写错根本没连上lib文件链接的是 x86 的库但工程是 x64或者反过来JSONCPP_DLL宏定义和库类型不匹配。排错时我一般先用 dumpbin 或 VS 的依赖查看器确认导入库的位数和接口符号。再检查预处理宏里是否多定义或漏定义了JSONCPP_DLL。如果是静态库删掉这个宏再重新编译如果是动态库在业务代码编译选项里加DJSONCPP_DLL。5.2#include json/json.h找不到头文件这个问题的本质通常是 include 根目录给错了。jsoncpp 的头文件路径是include/json/json.h所以你的编译器-I参数应该指到include这一层而不是指到include/json这一层。很多人试图图省事写成-I include/json然后代码里写#include json.h这样在当前版本偶尔能用但在某些构造路径下会因为相对引用json/forwards.h而失败。建议统一写#include json/json.hinclude 路径指到顶层。这样不管从哪个目录开始编译都不会崩。5.3 Linux 下的链接器静态库顺序问题静态库在链接时是有顺序的。原因是ld是按从左到右的顺序扫描目标文件如果它在处理某个目标文件时还没有看到所需符号的定义这个符号就会变成“未定义引用”。所以# 正确 g main.cpp -I include -L lib -ljsoncpp -o main # 错误大概率报 undefined reference g -ljsoncpp main.cpp -I include -L lib -o main排查这类问题只需看报错是否来自json_reader.cpp、json_value.cpp中的符号且命令里库文件位置明显靠前基本可以锁定是链接顺序问题。5.4 旧工具链编不了新版本的尴尬场景我曾在 CentOS 7 上试图编译 jsoncpp 1.9.x结果 gcc 4.8 对 C11 的支持不完整编译过程报了一堆语法错误。后来切到 devtoolset-7 提供的 gcc 才编过。这个现象也说明jsoncpp 对编译器版本其实是有“最低门槛”的1.9.x 建议 gcc 5.0 以上MSVC 建议 2015 以上。如果项目环境实在老旧两个选择一是升级编译器工具链二是改用老一点的 jsoncpp 分支。不过后者意味着失去新特性不到万不得已我不太推荐。5.5 ABI 匹配问题编译参数不能想当然最后聊一个比较隐蔽的问题。C 库不同于 C 库编译参数中的_GLIBCXX_USE_CXX11_ABI(gcc 5.1)、_ITERATOR_DEBUG_LEVEL(MSVC)、RTTI、异常处理方式都会影响最终二进制能不能无缝链接。比如 Ubuntu 18.04 和 20.04 上的默认 gcc 版本不同某些发行版会开启_GLIBCXX_USE_CXX11_ABI0如果你拿本机编译好的libjsoncpp.a放到另一个环境去链接轻则编译警告重则链接失败。所以“已编译”并不是一劳永逸它必须和消费方的工具链保持兼容。写 CMake 时把CMAKE_CXX_FLAGS和CMAKE_CXX_STANDARD明确固定下来比默认值更可靠。提示如果业务工程需要跨环境复用已编译库建议在文档里记录编译器版本、CMake 版本、C 标准、关键宏这几个字段。用一个简单的README或 CMake 注释就能避免几乎所有 ABI 问题。在我实际维护的工程里还会额外做一件事每个平台目录下放一个version.txt记录 jsoncpp tag、编译时间、编译器路径。虽然听起来没必要但真到了两三个月后重新捡起这套依赖时这几行字能省掉你大量回忆成本。编译 jsoncpp 本身不难难的是对版本、参数和工具链都心里有数。我建议你也试试用这个思路重新整理一遍自己的第三方库虽然第一次麻烦一点但后面接新项目时是真的省心。本文还有配套的精品资源点击获取