JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。JSON的应用场景非常广泛,在数据交互、配置保存、网页信息等场景中都可以使用JSON作为媒介。首先简要介绍JSON的语法规则:
在JavaScript语言中,一切都是对象。因此,任何支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等。JSON键值对是用来保存 JS 对象的一种方式,和JavaScript对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值,如下所示展示了JSON表示字符串、数字、数组的方式:
{
"Computer" : "ASUS",
"USBNumber" : 3,
"USBInfo" : [
{
"USBName" : "SanDisk",
"isEnable" : true
},
{
"USBName" : "Kingston",
"isEnable" : false
}
]
}
在C++中,有一个Jsoncpp开源库可以用来解析JSON,轻量又好用,下载地址:https://github.com/open-source-parsers/jsoncpp 。下载之后,主要使用的源码有src/下的json_reader.cpp、json_value.cpp、json_writer.cpp,它们的头文件位于include下的json/文件夹内。编译方法如下:
在src的lib_json/文件夹下新建文件Makefile,使用vim在Makefile中写入:
CC=g++
AR=ar
SEPARATOR="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
jsonlib=libjsoncpp.a
SOURCES = json_reader.cpp json_value.cpp json_writer.cpp
INCLUDE = -I../../include/
OBJS=$(patsubst %.cpp, %.o, $(SOURCES))
all: clean jsonlib
.cpp.o:
$(CC) ${OPTIMIZE} -o $@ -c $*.cpp $(INCLUDE) -std=c++11
@echo $(SEPARATOR);
jsonlib:$(OBJS)
$(AR) -r -v $(jsonlib) $(OBJS)
rm -rf *.o
@echo $(SEPARATOR);
clean:
rm -f ./$(jsonlib)
rm -f ./*.o
@echo $(SEPARATOR);
大体的目录结构应该都是这样,如有不同之处,修改成相应的目录结构即可。然后直接make,就可以生成libjsoncpp.a库。这样,就可以使用这个开源库来解析JSON了。本篇首先介绍最常使用的json_value.cpp中的一部分,内容比较基础,可以当做对C++的一些基础内容进行一次复习。
在value.h中,类Json::Value主要构造和析构函数声明如下:
class JSON_API Value {
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
#if defined(JSON_HAS_INT64)
Value(Int64 value);
Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
Value(double value);
Value(const char* value);
Value(const char* begin, const char* end);
Value(const StaticString& value);
Value(const String& value);
#ifdef JSON_USE_CPPTL
Value(const CppTL::ConstString& value);
#endif
Value(bool value);
Value(const Value& other);
Value(Value&& other);
~Value();
...
};
其中,构造函数为ValueType参数的实现如下,可以看到,默认类型就是nullValue类型,也就是空,其余如果构造成相应的类型,如intValue,则给结构体ValueHolder的相应类型的参数默认的值设置成0,其余的声明的实现也一样,就是给这个结构体赋值为其他值。
union ValueHolder {
LargestInt int_;
LargestUInt uint_;
double real_;
bool bool_;
char* string_;
ObjectValues* map_;
} value_;
enum ValueType {
nullValue = 0, ///< 'null' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< array value (ordered list)
objectValue ///< object value (collection of name/value pairs).
};
Value::Value(ValueType type) {
static char const emptyString[] = "";
initBasic(type);
switch (type) {
case nullValue:
break;
case intValue:
case uintValue:
value_.int_ = 0;
break;
case realValue:
value_.real_ = 0.0;
break;
case stringValue:
// allocated_ == false, so this is safe.
value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));
break;
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues();
break;
case booleanValue:
value_.bool_ = false;
break;
default:
JSON_ASSERT_UNREACHABLE;
}
}
根据源码中的构造函数,构造JSON时的示例如下所示,打印JSON时,可以将JSON转为string类型,再转为char*类型打印:
#include "json/json.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
int main()
{
Json::Value message0; //nullValue
Json::Value message1(1); //Int、UInt、Int64、UInt64
Json::Value message2(4.2); //double
char ch[5] = "abcd";
Json::Value message3(ch); //char* value
char* begin = ch;
char* end = ch + 3;
Json::Value message4(begin, end); //char* begin, char* end
std::string str("string");
Json::Value message5(str); //String
Json::Value message6(false); //bool
Json::Value json(true);
Json::Value message7(json); //Value& other
Json::Value&& json1 = 100;
Json::Value message8(json1); //Value&& other
printf("message1 = %s\n", message1.toStyledString().c_str());
printf("message2 = %s\n", message2.toStyledString().c_str());
printf("message3 = %s\n", message3.toStyledString().c_str());
printf("message4 = %s\n", message4.toStyledString().c_str());
printf("message5 = %s\n", message5.toStyledString().c_str());
printf("message6 = %s\n", message6.toStyledString().c_str());
printf("message7 = %s\n", message7.toStyledString().c_str());
printf("message8 = %s\n", message8.toStyledString().c_str());
return 0;
}
打印结果如下所示:
./jsontest
message1 = 1
message2 = 4.2000000000000002
message3 = "abcd"
message4 = "abc"
message5 = "string"
message6 = false
message7 = true
message8 = 100
Value类中,还对赋值号进行了重载,使得我们可以直接把一个JSON对象赋值给另一个JSON对象:
Value& operator=(const Value& other);
Value& operator=(Value&& other);
//示例
Json::Value message9 = message4;
Json::Value message10 = json1;
//结果
message9 = "abc"
message10 = 100
Value类常用的还有swap函数和copy函数,分别用作对JSON对象的交换和拷贝,如下:
//声明
void swap(Value& other);
void copy(const Value& other);
//实现(摘要)
void Value::swap(Value& other) { //swap主要就是调用了std中的swap函数,进行一些值的交换操作
swapPayload(other);
/*swapPayload:
std::swap(bits_, other.bits_);
std::swap(value_, other.value_);
*/
std::swap(comments_, other.comments_);
std::swap(start_, other.start_);
std::swap(limit_, other.limit_);
}
void Value::copy(const Value& other) {
copyPayload(other);
/*
void Value::dupPayload(const Value& other) { //copy函数对类型进行了判断,如果是基本类型,则直接进行拷贝操作,如果是对象或字符串string类型,则进行相应的深拷贝操作。具体的实现思想就是深拷贝的那套流程。
setType(other.type());
setIsAllocated(false);
switch (type()) {
case nullValue:
case intValue:
case uintValue:
case realValue:
case booleanValue:
value_ = other.value_;
break;
case stringValue:
if (other.value_.string_ && other.isAllocated()) {
unsigned len;
char const* str;
decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
&str);
value_.string_ = duplicateAndPrefixStringValue(str, len);
setIsAllocated(true);
} else {
value_.string_ = other.value_.string_;
}
break;
case arrayValue:
case objectValue:
value_.map_ = new ObjectValues(*other.value_.map_);
break;
default:
JSON_ASSERT_UNREACHABLE;
}
}
*/
delete[] comments_;
dupMeta(other);
}
//使用示例
Json::Value message11("message11");
message11.swap(message10);
Json::Value message12;
message12.copy(message10);
//结果
message10 = "message11"
message11 = 100
message12 = "message11"
Value类中对"<"、"<="、">="、">"、"=="、"!="进行了重载,重载这些符号的原理与我们写其他类重载符号一致,可以根据源码复习下,并提供了compare函数用于比较两个JSON对象的大小,声明及部分实现如下:
//声明
bool operator<(const Value& other) const;
bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const;
bool operator>(const Value& other) const;
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
int compare(const Value& other) const;
//实现(摘要)
bool Value::operator<(const Value& other) const {
int typeDelta = type() - other.type();
if (typeDelta)
return typeDelta < 0 ? true : false;
switch (type()) {
case nullValue:
return false;
case intValue: //对于基本数据类型,直接判断是否“<”即可
return value_.int_ < other.value_.int_;
case uintValue:
return value_.uint_ < other.value_.uint_;
case realValue:
return value_.real_ < other.value_.real_;
case booleanValue:
return value_.bool_ < other.value_.bool_;
case stringValue: {
if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
if (other.value_.string_)
return true;
else
return false;
}
unsigned this_len;
unsigned other_len;
char const* this_str;
char const* other_str;
decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
&this_str);
decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
&other_str);
unsigned min_len = std::min<unsigned>(this_len, other_len);
JSON_ASSERT(this_str && other_str);
int comp = memcmp(this_str, other_str, min_len); //对于字符串对象类,前面是做字符串提取的操作,然后使用memcmp进行字符串的比较,根据比较结果确定是否"<"
if (comp < 0)
return true;
if (comp > 0)
return false;
return (this_len < other_len);
}
case arrayValue:
case objectValue: {
int delta = int(value_.map_->size() - other.value_.map_->size());
if (delta)
return delta < 0;
return (*value_.map_) < (*other.value_.map_);
}
default:
JSON_ASSERT_UNREACHABLE;
}
return false; // unreachable
}
bool Value::operator<=(const Value& other) const { return !(other < *this); } //有了"<"的重载,这里只需要直接使用,变通下逻辑即可,后面几个符号重载也是如此
bool Value::operator>=(const Value& other) const { return !(*this < other); }
bool Value::operator>(const Value& other) const { return other < *this; }
bool Value::operator==(const Value& other) const {
if (type() != other.type())
return false;
switch (type()) {
case nullValue:
return true;
case intValue:
return value_.int_ == other.value_.int_;
case uintValue:
return value_.uint_ == other.value_.uint_;
case realValue:
return value_.real_ == other.value_.real_;
case booleanValue:
return value_.bool_ == other.value_.bool_; //对于基本数据类型,直接判断是否“==”即可
case stringValue: {
if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
return (value_.string_ == other.value_.string_);
}
unsigned this_len;
unsigned other_len;
char const* this_str;
char const* other_str;
decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
&this_str);
decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
&other_str);
if (this_len != other_len)
return false;
JSON_ASSERT(this_str && other_str);
int comp = memcmp(this_str, other_str, this_len);//通重载"<"一样,对于字符串对象类,前面是做字符串提取的操作,然后使用memcmp进行字符串的比较,根据比较结果是否为0确定是否"=="
return comp == 0;
}
case arrayValue:
case objectValue:
return value_.map_->size() == other.value_.map_->size() &&
(*value_.map_) == (*other.value_.map_);
default:
JSON_ASSERT_UNREACHABLE;
}
return false; // unreachable
}
bool Value::operator!=(const Value& other) const { return !(*this == other); }
//compare的实现可以直接使用上面重载过的符号进行
int Value::compare(const Value& other) const {
if (*this < other)
return -1;
if (*this > other)
return 1;
return 0;
}
//示例
if(message12 == message10)
{
printf("message12 == message10, message12 = %s, message10 = %s\n", message12.toStyledString().c_str(), message10.toStyledString().c_str());
}
if(message9 < message10)
{
printf("message9 < message10, message9 = %s, message10 = %s\n", message9.toStyledString().c_str(), message10.toStyledString().c_str());
}
int compareRet = message9.compare(message10);
printf("compareRet = %d\n", compareRet);
//结果
message12 == message10, message12 = "message11"
, message10 = "message11"
message9 < message10, message9 = "abc"
, message10 = "message11"
compareRet = -1 //<0表示小于,==0表示等于,>0表示大于
接下来在源码中,就是一些比较重要的重载"[]"函数,有了各式各样的重载"[]"函数后,JSON对象就可以以键值对的形式进行读取和写入了,下面的代码展示了重载"[]"的多种形式,其中有很多相同参数的重载,但只是函数前加了const修饰,有些没有const修饰,这个的主要作用就是用来设定使JSON键值可以做左值和右值,做左值时,可以进行赋值和修改,这里的实现主要运用到了std::map,在JSON键值对中,会在map中对JSON左值中的键进行查找,如果键存在,则直接根据键从map中进行读取值,若键不存在,则插入map中。做右值时,只可以根据键从map中进行读取。
//声明(最常用)
Value& operator[](ArrayIndex index);
Value& operator[](int index);
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
Value& operator[](const char* key);
const Value& operator[](const char* key) const;
Value& operator[](const String& key);
const Value& operator[](const String& key) const;
//实现(摘要)
//获取或插入数组类型元素,注意在使用到角标为0的时候,必须是无符号型,即value[0u]
Value& Value::operator[](ArrayIndex index) {
JSON_ASSERT_MESSAGE(
type() == nullValue || type() == arrayValue,
"in Json::Value::operator[](ArrayIndex): requires arrayValue");
if (type() == nullValue)
*this = Value(arrayValue);
CZString key(index);
auto it = value_.map_->lower_bound(key);
if (it != value_.map_->end() && (*it).first == key) //根据index的序号,查找map
return (*it).second; //查找成功,就直接返回它的值
ObjectValues::value_type defaultValue(key, nullSingleton());
it = value_.map_->insert(it, defaultValue); //未找到,则进行插入(赋值)
return (*it).second;
}
//获取数组类型的元素(只读)
const Value& Value::operator[](ArrayIndex index) const {
JSON_ASSERT_MESSAGE(
type() == nullValue || type() == arrayValue,
"in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
if (type() == nullValue)
return nullSingleton();
CZString key(index);
ObjectValues::const_iterator it = value_.map_->find(key);
if (it == value_.map_->end())
return nullSingleton(); //根据index的序号,查找map,查到返回值,查不到返回空
return (*it).second;
}
//也是获取数组类型的元素,与ArrayIndex相同,只是这里的参数类型是int,上面的是arrayIndex类型
Value& Value::operator[](int index) {
JSON_ASSERT_MESSAGE(
index >= 0,
"in Json::Value::operator[](int index): index cannot be negative");
return (*this)[ArrayIndex(index)];
}
const Value& Value::operator[](int index) const {
JSON_ASSERT_MESSAGE(
index >= 0,
"in Json::Value::operator[](int index) const: index cannot be negative");
return (*this)[ArrayIndex(index)];
}
//根据JSON键key获取值(只读)
const Value& Value::operator[](const char* key) const {
Value const* found = find(key, key + strlen(key));
if (!found)
return nullSingleton();
return *found;
}
//根据JSON键key获取值(只读)
Value const& Value::operator[](const String& key) const {
Value const* found = find(key.data(), key.data() + key.length());
if (!found)
return nullSingleton();
return *found;
}
Value& Value::operator[](const char* key) {
return resolveReference(key, key + strlen(key)); //resolveReference实现见下面
}
Value& Value::resolveReference(char const* key, char const* end) {
JSON_ASSERT_MESSAGE(
type() == nullValue || type() == objectValue,
"in Json::Value::resolveReference(key, end): requires objectValue");
if (type() == nullValue)
*this = Value(objectValue);
CZString actualKey(key, static_cast<unsigned>(end - key),
CZString::duplicateOnCopy);
auto it = value_.map_->lower_bound(actualKey);
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second; //根据key值,查找map,查到返回值
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
it = value_.map_->insert(it, defaultValue); //查找不到时,插入
Value& value = (*it).second;
return value;
}
Value& Value::operator[](const String& key) {
return resolveReference(key.data(), key.data() + key.length());
}
//示例
//JSON数组
Json::Value jsonMess0 = Json::Value::null;
Json::Value jsonMess1 = Json::Value::null;
jsonMess0[0u] = "array0";
jsonMess0[1] = 250;
jsonMess1[0u] = jsonMess0[1];
jsonMess1[1] = jsonMess0[0u];
printf("jsonMess0 = %s, jsonMess1 = %s\n", jsonMess0.toStyledString().c_str(), jsonMess1.toStyledString().c_str());
//结果
jsonMess0 = [
"array0",
250
]
, jsonMess1 = [
250,
"array0"
]
//JSON键值对
Json::Value jsonMess2 = Json::Value::null;
jsonMess2["name"] = "Tom";
jsonMess2["age"] = 18;
jsonMess2["isMarried"] = false;
printf("jsonMess2 = %s\n", jsonMess2.toStyledString().c_str());
jsonMess2["name"] = "Bob";
printf("jsonMess2 = %s\n", jsonMess2.toStyledString().c_str());
//结果,(内部还是使用map实现的)
jsonMess2 = {
"age" : 18,
"isMarried" : false,
"name" : "Tom"
}
jsonMess2 = {
"age" : 18,
"isMarried" : false,
"name" : "Bob"
}
//JSON数组和键值对结合
Json::Value jsonMess3 = Json::Value::null;
jsonMess3[0u]["Computer"] = "ASUS";
jsonMess3[0u]["MemorySize"] = 500 * 1024 * 1024;
jsonMess3[0u]["USB"][0u] = "SanDisk";
jsonMess3[0u]["USB"][1] = "Kingston";
jsonMess3[1]["Computer"] = "Apple";
jsonMess3[1]["MemorySize"] = 256 * 1024 * 1024;
jsonMess3[1]["USB"][0u] = "Toshiba";
jsonMess3[1]["USB"][1] = "WestData";
//这里故意直接跳到不连续序号,且随机赋值
jsonMess3[3]["UUUUU"][3] = "XXXXX";
printf("jsonMess3 = %s\n", jsonMess3.toStyledString().c_str());
//结果:少了的角标为2的元素为null,且角标为3的元素只有赋值了的地方有值,其余为null,这样就能把我们要表达的东西原模原样表达出来
jsonMess3 = [
{
"Computer" : "ASUS",
"MemorySize" : 524288000,
"USB" :
[
"SanDisk",
"Kingston"
]
},
{
"Computer" : "Apple",
"MemorySize" : 268435456,
"USB" :
[
"Toshiba",
"WestData"
]
},
null,
{
"UUUUU" :
[
null,
null,
null,
"XXXXX"
]
}
]
关于json_value中的其他源码介绍,下次继续分解。