【C++】jsoncpp库问题一二

qianqianxiao 2019-07-01

Json是数据交换常用的一种数据格式。C++标准库并没有包含任何Json标准的实现,所以我们要借助于一些第三方库,jsoncpp就是一个常用的库。由于工作中经常用jsoncpp,发现这个库问题不少,该篇文章本意是想深度吐槽一下这个函数库,但在寻找证据的过程中发现部分问题是由版本造成的,所以对问题进行一些说明,希望能帮助遇到同样问题的朋友。

异常导致程序终止

这也是我在工作中遇到的问题:在程序处理非法json串时,直接导致服务程序终止,即使使用try块包围这段代码,仍无法成功捕获异常。深入阅读它的实现代码才发现,jsoncpp的异常处理是比较原始的,遇到异常直接assert,所以程序直接就终止了。对于简单的小程序来说,这无可厚非,但对于后端服务来说,这个问题非常严重,因为造成了服务不可用。

经过再度挖掘,发现开发环境用的jsoncpp版本是0.5.0,所以异常处理机制原始;实际上在jsoncpp 0.8.3开始就对异常处理进行了改变,通过定义JSON_USE_EXCEPTION宏改变了类型无效的行为:

If defined, indicates that Json use exception to report invalid type manipulation instead of C assert macro.

在最新版的1.8.4里,JSON_USE_EXCEPTION=1已经是默认行为了,除了注释错误仍然调用assert外,基本上其他所有的错误都会抛出异常,不再assert了:

If non-zero, the library uses exceptions to report bad input instead of C assertion macros. The default is to use exceptions.

所以,最简单的方案就是将库升级为最新版。如果因为一些原因无法升级函数库,那最稳妥的方式就是在使用数据之前进行严格的数据检查,除此之外没有其他更好的方式。

对于哪些错误可能导致异常,在jsoncpp.cpp文件里直接搜关键字JSON_ASSERTJSON_FAIL_MESSAGEassert,找到对应异常所在的代码,可以让你知道哪些具体情况会导致异常,以及哪些是可捕捉的,哪些会引起程序终止的。

蹩脚的使用方式

说“蹩脚”,主要还是因为它提供的API太老套,明显没跟上现代C++的步伐,同时跟C++的各类容器交互困难,大部分情况需要人工写代码。比如:

  • 缺乏直观的初始化操作

现代C++增强了直观的初始化,比如map的初始化

std::map<int,int> map {{1,1},{2,2}};

但jsoncpp并没有提供这类json常量的初始化,我们只能通过:

Json::Value root;
    Json::FastWriter writer;

    root["name"] = "ideami";

    std::string json = writer.write(root);

    std::cout << json << std::endl;

人工构造,或者使用:

Json::Reader reader;
    Json::Value root;
    std::string jsonStr = "{\"name\":\"ideami\"}";
    if (!reader.parse(jsonStr, root)) {
        return -1;
    }

缺乏直观性。

  • 不提供STL容器到Json结构的转换

std::vector<int>类型的数组,若生成json数组,我们只能:

#include <iostream>
#include "json/json.h"
#include <string>

int main() {
    std::vector<int> v {1,2,3,4};

    Json::Value array;
    Json::FastWriter writer;

    for (auto e : v)
    {
        array.append(e);
    }

    std::cout << writer.write(array) << std::endl;
}

如果能提供直接转换的功能就方便了...

整体上,jsoncpp提供的API比较难用,上面用的API都是老版的API,其提供的新版APIJson::CharReaderBuilderJson::StreamWriterBuilder等易用性也很差。

在易用性上,JSON for Modern C++是一个更好的选择。

请继续关注我的公众号文章
【C++】jsoncpp库问题一二

相关推荐