当你第一次在Linux环境下用C语言处理JSON数据时,可能会被各种动态库问题绊住脚步。上周我在为物联网设备开发配置解析模块时,就遭遇了经典的"error while loading shared libraries"问题——明明按照教程编译安装了json-c,程序却死活找不到动态库。这种看似简单却令人抓狂的问题,恰恰暴露了Linux动态链接机制的微妙之处。
当你的程序提示"error while loading shared libraries: libjson-c.so.5"时,系统实际上是在告诉你:"我知道你需要这个库,但我找不到它"。这时候需要像侦探一样排查问题:
bash复制# 确认库文件是否真的存在
find /usr/local/lib/ -name "libjson-c.so*"
# 检查库文件权限
ls -l /usr/local/lib/libjson-c.so.5
# 查看程序依赖的库
ldd ./your_program | grep json-c
典型的输出可能显示库文件确实存在于/usr/local/lib,但你的程序却找不到它。这是因为Linux动态链接器默认不会搜索这个目录。
Linux的动态链接器(ld.so)按照固定顺序搜索共享库:
当你的json-c安装在/usr/local/lib时,问题就出现了——这个目录通常不在默认搜索路径中。我有三种方法解决这个问题:
方法对比表:
| 解决方案 | 命令示例 | 适用范围 | 持久性 | 影响范围 |
|---|---|---|---|---|
| 创建符号链接 | sudo ln -s /usr/local/lib/libjson-c.so.5 /usr/lib/ |
单机环境 | 永久 | 全局 |
| 修改LD_LIBRARY_PATH | export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH |
开发测试 | 临时 | 当前会话 |
| 更新ld.so.conf | `echo '/usr/local/lib' | sudo tee -a /etc/ld.so.conf && sudo ldconfig` | 生产环境 | 永久 |
提示:在Docker容器中部署时,推荐使用方法3,确保容器重建后配置依然有效
json-c提供了简洁的API来创建和操作JSON数据。下面这个例子展示了如何构建一个包含嵌套结构的JSON:
c复制#include <json-c/json.h>
#include <stdio.h>
int main() {
// 创建根对象
struct json_object *root = json_object_new_object();
// 添加基本类型
json_object_object_add(root, "deviceID", json_object_new_string("ESP32-001"));
json_object_object_add(root, "temperature", json_object_new_double(26.5));
json_object_object_add(root, "active", json_object_new_boolean(1));
// 创建嵌套数组
struct json_object *sensors = json_object_new_array();
json_object_array_add(sensors, json_object_new_string("DHT22"));
json_object_array_add(sensors, json_object_new_string("DS18B20"));
// 添加数组到根对象
json_object_object_add(root, "sensors", sensors);
// 格式化输出
printf("%s\n", json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
json_object_put(root);
return 0;
}
输出结果:
json复制{
"deviceID": "ESP32-001",
"temperature": 26.5,
"active": true,
"sensors": [
"DHT22",
"DS18B20"
]
}
解析JSON时,类型检查至关重要。这段代码展示了安全的解析方式:
c复制void parse_config(const char *json_str) {
struct json_object *parsed = json_tokener_parse(json_str);
if (!parsed) {
fprintf(stderr, "Failed to parse JSON\n");
return;
}
struct json_object *config;
if (json_object_object_get_ex(parsed, "config", &config)) {
// 处理config对象
struct json_object *version;
if (json_object_object_get_ex(config, "version", &version)) {
if (json_object_is_type(version, json_type_string)) {
printf("Config version: %s\n", json_object_get_string(version));
}
}
}
json_object_put(parsed);
}
常见陷阱:
json-c使用引用计数管理内存,这可能导致一些隐蔽的问题:
c复制struct json_object *create_nested_json() {
struct json_object *root = json_object_new_object();
struct json_object *child = json_object_new_object();
json_object_object_add(root, "child", child);
// 错误:此时child的引用计数已经是2
json_object_put(child); // 可能导致use-after-free
return root; // root现在持有悬垂指针
}
正确的做法是:
c复制struct json_object *create_nested_json_correct() {
struct json_object *root = json_object_new_object();
struct json_object *child = json_object_new_object();
json_object_object_add(root, "child", child);
// 不需要单独put child,root会管理它的生命周期
return root;
}
对于大文件,可以使用json_tokener的增量解析模式:
c复制#define BUF_SIZE 4096
void parse_large_json(FILE *fp) {
struct json_tokener *tokener = json_tokener_new();
char buf[BUF_SIZE];
while (fgets(buf, BUF_SIZE, fp)) {
enum json_tokener_error jerr;
struct json_object *obj = json_tokener_parse_ex(tokener, buf, strlen(buf));
if (obj) {
// 处理完整对象
process_json_object(obj);
json_object_put(obj);
} else if ((jerr = json_tokener_get_error(tokener)) != json_tokener_continue) {
fprintf(stderr, "Parse error: %s\n", json_tokener_error_desc(jerr));
break;
}
}
json_tokener_free(tokener);
}
在嵌入式开发中,JSON常用来存储设备配置。这个例子展示了如何实现一个健壮的配置加载器:
c复制struct device_config {
char *ssid;
char *password;
int sampling_interval;
double threshold;
};
int load_config(const char *path, struct device_config *cfg) {
FILE *fp = fopen(path, "r");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
long len = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *json_str = malloc(len + 1);
fread(json_str, 1, len, fp);
json_str[len] = '\0';
fclose(fp);
struct json_object *parsed = json_tokener_parse(json_str);
free(json_str);
if (!parsed) return -2;
struct json_object *wifi, *val;
if (json_object_object_get_ex(parsed, "wifi", &wifi)) {
if (json_object_object_get_ex(wifi, "ssid", &val))
cfg->ssid = strdup(json_object_get_string(val));
if (json_object_object_get_ex(wifi, "password", &val))
cfg->password = strdup(json_object_get_string(val));
}
if (json_object_object_get_ex(parsed, "sampling_interval", &val))
cfg->sampling_interval = json_object_get_int(val);
if (json_object_object_get_ex(parsed, "threshold", &val))
cfg->threshold = json_object_get_double(val);
json_object_put(parsed);
return 0;
}
不同平台对JSON的实现可能有细微差别。这段代码处理了可能的兼容性问题:
c复制// 检查json-c版本
#if JSON_C_MINOR_VERSION >= 13
// 使用新API
json_object_set_serializer(obj, custom_serializer, NULL, NULL);
#else
// 回退方案
char *tmp = custom_serialize(obj);
json_object_set_string(obj, tmp);
free(tmp);
#endif
在解决那个动态库问题后的三个月里,我在三个不同的物联网项目中使用了json-c,从最初的手忙脚乱到现在能从容处理各种边界情况。最让我意外的是,当初那个看似棘手的动态库问题,现在回想起来反而是让我深入理解Linux系统机制的最佳契机。