json其实不难,只需了解一点,就能轻松玩转它

工作过程中,经常需要使用json这种轻量级的数据交换格式,例如,通过它可以组织数据保存到配置文件,客户端和服务端通过json格式来进行通信等,但是,针对特定的需求场景,需要设计怎样的json格式比较合适呢,json到底可以支持多少种格式呢,有没有一种简单的概括,就能让使用者轻松使用它呢!

一般知识都有基本的理论结构,所以,本文首先将说明json的基本知识点,然后基于开源软件jsoncpp来说明如何构建/读写json,   再分享个人的使用心得,最后再总结json的关键要点,理解了这一点,玩转json不是问题。

一、Json简介

Json是轻量级的数据交换格式,它便于阅读和编写,它是完全独立于程序语言的文本格式。

二、Json结构

Json有两个结构, 分别是“名称/值”对的集合和值的有序列表。“名称/值”对的集合可以简单理解为对象集合,而值的有序列表可以理解为数组。

这里举一个“名称/值”对的集合的例子,它是以左大括号开始,以右大括号结束,中间是由多个“名称/值”对组成,各个“名称/值”对之间用逗号隔开。

{
    "cpu_name" : "special",
    "cpu_temp" : 40
}

举一个“值的有序列表”的例子,它是以左中括号开始,以右中括号结束,中间是由多个值组成,各个值之间用逗号隔开。

["apple", "pear", "banana "]

三、Json形式

Json主要由三种形式,分别为对象(object),  数组(array),  值(value)。

对象(object)是“名称/值”对集合,名称于值之间通过冒号隔开,另外对象是以左大括号开始,以右大括号结束。

数组(array)是值的有序集合,它是以左中括号开始,以右中括号结束。

值(value)可以是字符串(string)、数值(number)、对象(object)、数组(array)、true、false、null。这里我们会发现对象(object)里面有值(value),  数组(array)里面也有值(value),  而值(value)又包含有对象和数组,所以它们是可以嵌套的。

Json就是由上面简单的元素来组建复杂的信息元素。

四、Json例子

jsoncpp是C++语言编写的开源json库,通过该库,我们可以很容易的构建、读写json。接下来就基于jsoncpp来解释几个构建、读取json的例子。通过例子可以对json有更深的理解。jsoncpp最基本的对象就是Json::Value。

构建一个最简单的对象,然后输出整个json信息,最后读取json值,先调用isMember判断名称是否为root成员,如果是的话,那么就读取输出。

Json::Value root;
root["result"] = "true";

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(root);
LOG(INFO) << "str_json: " << str_json.c_str();

if (root.isMember("result"))
{
    LOG(INFO) << "root[\"result\"] = " << root["result"];
}

输出的日志信息如下所示,大括号包含了一个“名称/值”对。

2020-05-02 17:59:32,670 INFO  [default] str_json: {
   "result" : "true"
}

2020-05-02 17:59:32,670 INFO  [default] root["result"] = "true"

构建嵌套对象,第一个根“名称/值”对中的“值”又是一个对象。

Json::Value root;
Json::Value value;
value["cpu_name"] = "arm";
root["cpu_info"] = value;

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(root);
LOG(INFO) << "str_json: " << str_json.c_str();

if (root["cpu_info"].isMember("cpu_name"))
{
    LOG(INFO) << "root[\"cpu_info\"][\"cpu_name\"] = " << root["cpu_info"]["cpu_name"];
}

输出的日志信息如下所示

2020-05-02 17:59:32,670 INFO  [default] str_json: {
   "cpu_info" : {
      "cpu_name" : "arm"
   }
}

2020-05-02 17:59:32,670 INFO  [default] root["cpu_info"]["cpu_name"] = "arm"

构建三层嵌套对象,第一个根“名称/值”对中的“值”是一个对象,而该对象的“值”又是一个对象。依次类推,可以构建更多层的嵌套对象。

Json::Value root;
Json::Value value_01;
Json::Value value_02;

value_02["cell_number"]  = 255;
value_01["eye"] = value_02;
root["body"] = value_01;

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(root);
LOG(INFO) << "str_json: " << str_json.c_str();

if (root["body"]["eye"].isMember("cell_number"))
{
    LOG(INFO) << "root[\"body\"][\"eye\"][\"cell_number\"]  = " << root["body"]["eye"]["cell_number"];
}

输出的日志信息如下所示

2020-05-02 17:59:32,670 INFO  [default] str_json: {
   "body" : {
      "eye" : {
         "cell_number" : 255
      }
   }
}

2020-05-02 17:59:32,670 INFO  [default] root["body"]["eye"]["cell_number"]  = 255

构建简单的数组,jsoncpp中构建数组是通过append的接口来创建的。读取数组之前,先调用isArray来判断对象是否为数组,如果是的话,再读取输出。这里需要注意数组的个数。从防御式编程的角度看,读取数组值之前,需要判断数组索引是否在有效范围内。

Json::Value array;
array.append("one");
array.append("two");

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(array);
LOG(INFO) << "str_json: " << str_json.c_str();

if (array.isArray())
{
    LOG(INFO) << "array.size(): " << array.size();
    LOG(INFO) << "array[0]: " << array[0];
    LOG(INFO) << "array[1]: " << array[1];
}

输出的日志信息如下所示,从这里我们也可以确定数组是可以单独作为独立json串出现的。之前一直都有一个误区,就是认为json一定要用大括号包括起来。

2020-05-02 17:59:32,670 INFO  [default] str_json: [ "one", "two" ]

2020-05-02 17:59:32,671 INFO  [default] array.size(): 2
2020-05-02 17:59:32,671 INFO  [default] array[0]: "one"
2020-05-02 17:59:32,671 INFO  [default] array[1]: "two"

构建对象和数组组成的json。首先创建一个数组,然后将其作为对象的值。

Json::Value array;
array.append("one");
array.append("two");
array.append("three");

Json::Value root;
root["number"] = array;

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(root);
LOG(INFO) << "str_json: " << str_json.c_str();

if (root["number"].isArray())
{
    LOG(INFO) << "root[\"number\"].size(): " << root["number"].size();
    LOG(INFO) << "root[\"number\"][0]: " << root["number"][0];
    LOG(INFO) << "root[\"number\"][1]: " << root["number"][1];
    LOG(INFO) << "root[\"number\"][2]: " << root["number"][2];
}

输出的日志信息如下所示

2020-05-02 17:59:32,671 INFO  [default] str_json: {
   "number" : [ "one", "two", "three" ]
}

2020-05-02 17:59:32,671 INFO  [default] root["number"].size(): 3
2020-05-02 17:59:32,671 INFO  [default] root["number"][0]: "one"
2020-05-02 17:59:32,671 INFO  [default] root["number"][1]: "two"
2020-05-02 17:59:32,671 INFO  [default] root["number"][2]: "three"

最后再构建稍微复杂一点的json串,它是由对象、数组、对象来组成的,即对象的值是一个数组,而数组内部的值是由对象组成。

Json::Value root;
Json::Value array;
Json::Value value_01;
Json::Value value_02;

value_01["peripheral"] = 1;
value_01["patient"] = 2;

value_02["image"] = 3;
value_02["auto"] = 4;

array.append(value_01);
array.append(value_02);

root["department"] = array;

Json::StyledWriter styled_writer;
std::string str_json = styled_writer.write(root);
LOG(INFO) << "str_json: " << str_json.c_str();

if (root["department"].isArray())
{
    LOG(INFO) << "root[\"department\"].size(): " << root["department"].size();
    LOG(INFO) << "root[\"department\"][0][\"patient\"]: " << root["department"][0]["patient"];
    LOG(INFO) << "root[\"department\"][1][\"auto\"]: " << root["department"][1]["auto"];
}

输出的日志信息如下所示

2020-05-02 17:59:32,671 INFO  [default] str_json: {
   "department" : [
      {
         "patient" : 2,
         "peripheral" : 1
      },
      {
         "auto" : 4,
         "image" : 3
      }
   ]
}

2020-05-02 17:59:32,671 INFO  [default] root["department"].size(): 2
2020-05-02 17:59:32,671 INFO  [default] root["department"][0]["patient"]: 2
2020-05-02 17:59:32,671 INFO  [default] root["department"][1]["auto"]: 4

五、使用心得

  1. 读取json值之前,先判断其有效性,可以结合断言机制,调用isMember或者isArray来进行判断。
  2. 使用数组的时候,需要特别注意数组下标。

六、总结

json主要是由对象或数组创建而成,而它们的嵌套使用就可以创建复杂的json串,根据特定场景的需求来创建适用的json格式。

发表评论

电子邮件地址不会被公开。 必填项已用*标注