json协议-Jsoncpp

Jsoncpp常规用法

jsoncpp github地址: https://github.com/open-source-parsers/jsoncpp.git

目录

    • Jsoncpp常规用法
      • 1. 安装
      • 2. 序列化与反序列化
        • 2.1 生成json对象
        • 2.2 读取json对象
        • 2.3 写入字符串或文件
        • 2.4 从文件/字符串中反序列化
      • 3. 总结

1. 安装

下载源码后,可以通过多种方式进行 安装,例如cmake

mkdir build
cd build
cmake ..
make
make install #安装到系统 

./build/lib文件夹中是静态库和动态库;当然可以同时将源码中的include文件夹拷贝到项目,可以使用这种源码依赖的方式,避免编译的时候不同版本之间有不兼容的情况。
可行的目录树为:

├── build
├── CMakeLists.txt
├── include
│   ├── json
│   │   ├── allocator.h
│   │   ├── assertions.h
│   │   ├── CMakeLists.txt
│   │   ├── config.h
│   │   ├── forwards.h
│   │   ├── json_features.h
│   │   ├── json.h
│   │   ├── PreventInBuildInstalls.cmake
│   │   ├── PreventInSourceBuilds.cmake
│   │   ├── reader.h
│   │   ├── value.h
│   │   ├── version.h
│   │   └── writer.h
│   ├── libjsoncpp.a
│   ├── libjsoncpp.so -> libjsoncpp.so.25
│   ├── libjsoncpp.so.1.9.5
│   └── libjsoncpp.so.25 -> libjsoncpp.so.1.9.5
└── json_test.cc

对应的CMakeLists.txt可以为:

cmake_minimum_required(VERSION 3.6)
set(app_name json_test)
project(${app_name})

include_directories(${CMAKE_SOURCE_DIR}/include)
link_directories(${CMAKE_SOURCE_DIR}/include)

add_executable(${app_name} json_test.cc)

target_link_libraries(${app_name} libjsoncpp.a)

2. 序列化与反序列化

2.1 生成json对象

json可以理解为三部分:

  1. 普通值(数值,字符串,逻辑值,null)
  2. 数组 []
  3. 对象 {}
    例如有个json,是如下格式:
{
  "env_name": "测试",
  "node_name": "centos7",
  "dns_time": 0.2,
  "max_wait_time": 5,
  "redirect": true,
  "target_link": [
    "1.0.0.0:1410",
    "1.0.0.1:1411"
  ],
  "redirect_details": {
    "bj": "0.0.0.0",
    "sh": "0.0.0.1"
  },
  "null_obj":{}
}

jsoncpp生成该对象:

    Json::Value conf;
    // 一般值
    conf["env_name"] = "测试";
    conf["node_name"] = "centos7";
    conf["dns_time"] = 0.2;
    conf["max_wait_time"] = 5;
    conf["redirect"] = true;
	// 数组
    conf["target_link"] = Json::Value(Json::arrayValue);
    conf["target_link"].append("1.0.0.0:1410");
    conf["target_link"].append("1.0.0.1:1411");
	// 对象
    Json::Value redirect;
    redirect["bj"] = "0.0.0.0";
    redirect["sh"] = "0.0.0.1";
    conf["redirect_details"] = redirect;
	// 空对象
    conf["null_obj"] = Json::Value(Json::objectValue);
    return conf;

2.2 读取json对象

获取json对象后,希望获取各个值,有以下几种情况

  1. 已经json的结构, 例如是约定好的协议
  2. 已经部分json的结构, 例如约定好协议的新老版本,某个阶段只知道以往的结构
  3. 完全不知道结构,例如仅是数据变相中转

已知结构或者部分结构的情况下可以按照key值进行获取,或者使用jsonpath进行抽取
未知结构的情况一般也没必要读取,需要的话当然也可以使用递归遍历

  • 按照key读取
	// 获取上述json对象
	Json::Value conf = product();
    // 一般值/对象
    // 直接按照map的方式读取相当于 get default=NULL
    std::clog << conf.get("env_name", "default_env").asString() << std::endl;
    std::clog << conf["env_name"].asString() << std::endl;

    std::clog << conf.get("env_name_", "default_env").asString() << std::endl;
    std::clog << conf["env_name_"].asString() << std::endl; 
    // 数组
    auto read_arr = [](const Json::Value& arr) {
        if(!arr.isArray())return;
        for(Json::ArrayIndex index=0; index < arr.size(); ++index){
            std::clog << arr[index].asString() << std::endl;
            // 一般情况下,在获取值之前都需要判断一下值的类型
            // if(arr[index].isBool()){
            //     std::clog << arr[index].asBool() ? "true":"false";
            // }
        }
    };
    read_arr(conf["target_link"]);
  • jsonpath抽取
    jsoncpp 中的Path类用于jsonpath的抽取操作
    // jsonpath 抽取
    Json::Path path1(".redirect_details.sh");
    std::clog << "jsonpath:" << path1.resolve(conf).asString() << std::endl;

    Json::Path path2(".target_link[0]");
    std::clog << "jsonpath:" << path2.resolve(conf).asString() << std::endl;
  • 遍历
    递归遍历的过程实际上就是对于数组和对象进一步确定具体的值类型,直到值类型为一般类型(字符串,数值,bool,null)
void lookup(const Json::Value& root, const char* key){
    Json::ValueType type = root.type();
    if(type <= Json::booleanValue){
        std::clog <<"key=" << key <<",value=" << root.asString()<< std::endl;
    }else if(type == Json::arrayValue){
        for(Json::ArrayIndex index=0; index < root.size(); ++index){
            lookup(root[index], key);
        }
    }else{
        if(root.empty()){
            std::clog <<"key=" << key <<",value= "<< std::endl;
            return;
        }
        Json::Value::Members members = root.getMemberNames();
        for(const auto& member : members){
            lookup(root[member], member.c_str());
        }
    }
}

2.3 写入字符串或文件

  1. Json::FastWriter类或是Json::StyledWriter类可以将json对象序列化为json字符串,不同在于后者是带格式indent的转换。
std::string write_str(const Json::Value &input, bool style= false){
    if(!style){
        Json::FastWriter writer;
        return writer.write(input);
    }
    Json::StyledWriter writer;
    return writer.write(input);
}
  1. writer工厂, 新版本里面官方更推荐使用这种方式
    这里需要说明的是,
  • 通常情况下我们不会在某个函数中去声明一个Json::StreamWriterBuilder 对象, 一般是在某个命名空间内或某个类内初始化一个,同时做好配置,避免对象的多次创建和销毁。
  • emitUTF8 这个设置是为了解决jsoncpp将中文序列化为utf8字符串的情况,对比一下设置前后的不同;当然setting_中还包含格式化有关的多个设置
{"dns_time":0.20000000000000001,"env_name":"\u6d4b\u8bd5","max_wait_time":5,"node_name":"centos7","null_obj":{},"redirect":true,"redirect_details":{"bj":"0.0.0.0","sh":"0.0.0.1"},"target_link":["1.0.0.0:1410","1.0.0.1:1411"]}

{"dns_time":0.20000000000000001,"env_name":"测试","max_wait_time":5,"node_name":"centos7","null_obj":{},"redirect":true,"redirect_details":{"bj":"0.0.0.0","sh":"0.0.0.1"},"target_link":["1.0.0.0:1410","1.0.0.1:1411"]}
std::string write_str_factory(const Json::Value &input, bool style= false){
    Json::StreamWriterBuilder writer_builder;
    writer_builder["emitUTF8"] = true;
    if(!style)
        writer_builder["indentation"] = "";
    std::unique_ptr<Json::StreamWriter> writer(writer_builder.newStreamWriter());
    std::stringstream is;
    if(writer->write(input, &is) != 0){
        return "";
    }
    return is.str();
}
  • 写入文件
    std::fstream output_file("./conf_bak.json", std::ios::out | std::ios::trunc);
    output_file << write_str_factory(conf, true);

2.4 从文件/字符串中反序列化

  1. 从文件中读取
bool load_file(const char *file_path, std::string& content) {
    std::fstream conf_file(file_path, std::ios::in | std::ios::binary);
    if (!conf_file.is_open()) {
        std::cerr << "open conf file failed, path:" << file_path << std::endl;
        return false;
    }
    // 1. read file
    Json::Reader reader;
    Json::Value conf;
    if(!reader.parse(conf_file, conf)){
        std::cerr << "parse file content to json failed, path:" << file_path << std::endl;
        return false;
    }
    // 2. to string
    Json::FastWriter writer;
    content = writer.write(conf);
    std::clog << writer.write(conf) << std::endl;
    return true;
}

同样也可以使用工厂

    // 工厂1
    Json::CharReaderBuilder read_builder;
    std::unique_ptr<Json::CharReader> reader(read_builder.newCharReader());
    if(!reader.parse(file_path, conf,false)){
        return false;
    }

    // 工厂2
    std::string errors;
    if(!Json::parseFromStream(read_builder, conf_file, &conf, &errors)){
        return false;
    }
  1. 从字符串读取
bool parse_str(const std::string& content, Json::Value& output){
    // 1. Reader
    Json::Reader reader;
    if(!reader.parse(content, output)) return false;

    // 2. CharReaderBuilder
    Json::CharReaderBuilder read_builder;
    std::unique_ptr<Json::CharReader> reader(read_builder.newCharReader());
    if(!reader.parse(content, output, false)){
        return false;
    }
}

3. 总结

一般情况下,我们使用到的也就是生成一个json对象、读取对象的内容、序列化为字符串;从文件或者字符串中反序列化为对象读写。需要注意的有:

新版本中更推荐使用工厂类进行序列化与反序列化
对于中文序列化为utf8字符串时,在设置中调整一下 emitUTF8 这个选项为true
格式化读写,同样可以调整参数 indentation="" 或是 indentation=" "
jsonpath,使用Path类,语法中需要省去开头的$符号,语法详情 JSONPath
现在很多场景下为了追求性能大多都在使用rapidjson, 但是不得不说jsoncpp还是比较易用的

你可能感兴趣的:(C++入坑,json,c++)