2012年,在公司项目刚接触到要使用JSON时,几个前端项目都是用MFC开发的,没找到合适的JSON解析库。
索性自己开发一个简洁实用移植性强的json解析工具,主要达到以下目标:
1. 能够解析JSON字符串
2. 能够生成JSON字符串
3. 在项目中不是一个纯粹的JSON工具,可作为数据结构来使用
4. 内存自动释放
5. 路径表达式获取或设置 json结构中的某个字段
6. 能便捷的增加json字段
工具只包含两个文件:
gxx_base.h, gxx_base.cpp
点击下载源代码
如代码中问题和bug,欢迎指正。
请大家见谅使用gxx(我姓名的缩写)作为文件的前缀,这样做的原因是:个人原创工具类,也是为了和工程中其他文件做区分避免混淆。
为什么名字为gxx_base而不是gxx_json呢?
笔者曾经问过自己同样的问题,答案是:它不是纯粹的json解析器,它是一个很实用的数据结构容器。
实际上,从2012年至今, 在公司很多项目中笔者都使用到gxx_base里提供的工具, 经过5年的维护修改,现在已经很稳定了,想在分享给有需要的猿同胞。
下面开始进入正题:
引入到工程中,会报如下错误:
error C3861: “GXX_TRACE”: 即使使用参数相关的查找,也未找到标识符
解决方法: 打开 gxx_base.h, 然后重新定义自己的 GXX_PRINT宏, GXX_PRINT宏用来输出日志信息的, 请实现自定义的日志信息输出
如何解析 JSON字符串
示例1:解析下面这串 json,读取abc数组,读取key1对应的值
{"abc":[1,2,3,{"key1":456,"key2":[4,5,6]}]}
#include "gxx_base.h"
int _tmain(int argc, _TCHAR* argv[])
{
// gxx_base中所有对象的创建都需要用create方法
GxxDictionaryPtr jsonParse = GxxDictionary::create();
try{
jsonParse->initWithJsonText("{\"abc\":[1,2,3,{\"key1\":456,\"key2\":[4,5,6]}]}");
// 取出abc
GxxArrayPtr abcVal = jsonParse->arrayValueForKey("abc");
if (abcVal)
printf("abc=%s\n", abcVal->toJsonText(false).c_str());
// 取出key1
// 第一种方式
GxxDictionaryPtr abc3 = abcVal->dictionaryValueAtIndex(3);
if (abc3) {
std::string key1 = abc3->stringValueForKey("key1");
printf("key1=%s\n", key1.c_str());
}
// 第二中方式, 使用路劲表达式,key值不存在返回空字符串,
// 路径表达式请参考 valueForKeyPath注释
std::string key1 = jsonParse->stringValueForKeyPath("abc[3].key1");
printf("路径表达式,key1=%s\n", key1.c_str());
}
catch(GxxException& e) {
printf("json 解析异常\n");
}
getchar();
return 0;
}
示例2:解析下面的json串,读取学号为 002学生的信息。
{
"schoolName": "清华大学",
"students": [
{
"id": "001",
"name": "张三",
"age": "20"
},
{
"id": "002",
"name": "李四",
"age": "23"
},
{
"id": "003",
"name": "王五",
"age": "22"
}
]
}
#include "gxx_base.h"
int _tmain(int argc, _TCHAR* argv[])
{
GxxDictionaryPtr jsonParse = GxxDictionary::create();
try{
jsonParse->initWithJsonText("{\"schoolName\":\"清华大学\", \"students\":[{\"id\":\"001\",\"name\":\"张三\",\"age\":\"20\"},{\"id\":\"002\",\"name\":\"李四\",\"age\":\"23\"},{\"id\":\"003\",\"name\":\"王五\",\"age\":\"22\"}]}");
// 第一种方式, 取出students
GxxArrayPtr students = jsonParse->arrayValueForKey("students");
if (students) {
for (int i = 0; i < students->count(); i++) {
GxxDictionaryPtr student = students->dictionaryValueAtIndex(i);
if (student->stringValueForKey("id") == "002") {
printf("第一种方式,找到002的信息:%s\n", student->toJsonText().c_str());
break;
}
}
}
// 第二种方式,表达式获取
// 取出姓名可用 "students[@id=002].name"
GxxDictionaryPtr student = jsonParse->dictionaryValueForKeyPath("students[@id=002]");
if (student) {
printf("第二种种方式,找到002的信息:%s\n", student->toJsonText().c_str());
}
// 第三种方式,重建一个新的字典, 使用场景:当需要频繁查找时
// studentsDir中的每个学生信息和students中的每个学生信息的内存不发生变化
GxxDictionaryPtr studentsDir = students->makeDictionaryByKey("id");
if (studentsDir) {
GxxDictionaryPtr student = studentsDir->dictionaryValueForKey("002");
if (student) {
printf("第三种方式,找到002的信息:%s\n", student->toJsonText().c_str());
// 002的年龄有误,要从23岁改成22岁
student->setValueForKey(22, "age");
// 亲可以尝试,在 用第一种或第二种方式,看看002的年龄是否是 22岁.
printf("002更改后的年龄:%d", jsonParse->intValueForKeyPath("students[@id=002].age"));
}
}
}
catch(GxxException& e) {
printf("json 解析异常\n");
}
getchar();
return 0;
}
#ifndef __GXX_JSON__H__
#define __GXX_JSON__H__
#include
#include
#include
#ifndef G2X_CLASSES
#define G2X_CLASSES
#endif
#ifdef WINVER
#define GXX_PRINT(x) {do{GXX_TRACE(x);} while (0);}
#else
#define GXX_PRINT(x) {do{GXX_TRACE("%s",x);} while (0);}
#endif
#define gxx_int_t(x) GxxValue::create((long)(x))
#define gxx_long_t(x) GxxValue::create((long)(x))
#define gxx_ulong_t(x) GxxValue::create((unsigned long)(x))
#define gxx_int64_t(x) GxxValue::create((long long)(x))
#define gxx_float_t(x) GxxValue::create((double)(x))
#define gxx_bool_t(x) GxxValue::create((long)(x?1:0))
class GxxException {
public:
GxxException(const char* errorInfo) {
G2X_CLASSES(0);
error = errorInfo;
}
const char* errorInfo() const {
return error.c_str();
}
private:
std::string error;
};
template
class GxxAutoPtr
{
public:
typedef _Ty element_type;
/*explicit*/ GxxAutoPtr(_Ty* _Ptr = 0)
: _Myptr(_Ptr)
{
if (_Ptr) _Myptr->retain();
}
GxxAutoPtr(const GxxAutoPtr<_ty>& _Right)
{
if (_Myptr == _Right.get()){
if (_Myptr) _Myptr->retain();
}else{
_Myptr = _Right.get();
if (_Myptr) _Myptr->retain();
}
}
template
GxxAutoPtr(GxxAutoPtr<_other>& _Right)
{
if (_Myptr == _Right.get()){
if (_Myptr) _Myptr->retain();
}else{
_Myptr = _Right.get();
if (_Myptr) _Myptr->retain();
}
}
~GxxAutoPtr()
{
if (_Myptr) _Myptr->release();
}
_Ty& operator*() const
{ // return designated value
return (*_Myptr);
}
_Ty *operator->() const
{ // return pointer to class object
return (&**this);
}
_Ty* get() const
{
return _Myptr;
}
template
operator GxxAutoPtr<_other>()
{
return (GxxAutoPtr<_other>(*this));
}
operator _Ty* () const
{
return _Myptr;
}
GxxAutoPtr<_ty>& operator = (_Ty* _Ptr)
{
if (_Myptr == _Ptr)
return *this;
if (_Myptr) _Myptr->release();
_Myptr = _Ptr;
if (_Myptr) _Myptr->retain();
return *this;
}
GxxAutoPtr<_ty>& operator = (GxxAutoPtr<_ty>& _Right)
{
if (_Myptr == _Right.get())
return *this;
if (_Myptr) _Myptr->release();
_Myptr = _Right.get();
if (_Myptr) _Myptr->retain();
return *this;
}
private:
_Ty* _Myptr;
};
class G2X_CLASSES GxxKey {
public:
GxxKey() {
_key = 0;
}
~GxxKey() {
if (_key)
delete _key;
_key = 0;
}
GxxKey(const char* _right) {
_key = 0;
reset(_right);
}
GxxKey(const GxxKey& _right) {
_key = 0;
reset( _right );
}
const char* resize(unsigned int size) {
if (_key)
delete []_key;
_key = new char[size+1];
memset(_key, 0, size + 1);
return _key;
}
char& operator [](int i) {
return *(_key+i);
}
operator const char* () const{
return _key;
}
private:
void reset(const char* _right) {
if (_key)
delete []_key;
int len = (int)strlen(_right);
_key = new char[len+1];
memcpy(_key, _right, len + 1);
}
private:
char *_key;
};
inline
bool operator == (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) == 0;
}
inline
bool operator != (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) != 0;
}
inline
bool operator < (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) < 0;
}
inline
bool operator > (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) > 0;
}
inline
bool operator <= (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) <= 0;
}
inline
bool operator >= (const GxxKey& _left, const GxxKey& _right) {
return strcmp(_left, _right) >= 0;
}
typedef std::string CGxxValueString;
#define GXX_CREATE_FUNC(classTy) \
protected:\
classTy(){}\
public:\
static GxxAutoPtr create()\
{\
classTy* object = (new classTy);\
if (!object->init())\
{\
object->release();\
return (GxxAutoPtr((classTy*)0));\
}\
return (GxxAutoPtr(object));\
}
class G2X_CLASSES GxxObject;
class G2X_CLASSES GxxDictionary;
class G2X_CLASSES GxxArray;
class G2X_CLASSES GxxString;
class G2X_CLASSES GxxValue;
class G2X_CLASSES GxxValueMap;
typedef GxxAutoPtr GxxObjectPtr;
typedef GxxAutoPtr GxxDictionaryPtr;
typedef GxxAutoPtr GxxArrayPtr;
typedef GxxAutoPtr GxxStringPtr;
typedef GxxAutoPtr GxxValuePtr;
typedef GxxAutoPtr GxxValueMapPtr;
class G2X_CLASSES GxxObject
{
protected:
GxxObject();
public:
virtual ~GxxObject();
virtual void print() {};
void retain();
void release();
protected:
virtual bool init() { return true; }
virtual void objectReleased() {}
public:
virtual std::string describe(bool isUtf8=false) { return std::string(""); }
void _print(const char* x);
void _println(const char* x);
private:
int retainCount;
};
class G2X_CLASSES GxxDictionary : public GxxObject
{
GXX_CREATE_FUNC(GxxDictionary);
protected:
virtual bool init();
virtual void objectReleased();
public:
/* 用json格式的字符串初始化字典 */
bool initWithJsonText(const char* jsonText, bool isUtf8=false);
std::string toJsonText(bool isUtf8=false);
virtual std::string describe(bool isUtf8=false);
// 返回把字典转为json格式的字符串
std::string describe_d(int depth, bool bFormat,bool isUtf8);
GxxArrayPtr names();
GxxArrayPtr sortedNames();
int count();
virtual void print();
bool isKeyExist(const char* key);
GxxValue* valueForKey(const char* key);
/* 不区分key大小写的方式读取对应的值(本方法效率很低) */
GxxValue* valueForKeyNoCase(const char* key);
GxxValue* operator[](const char* key);
int intValueForKey(const char* key, int defaultValue = 0);
bool boolValueForKey(const char* key, bool defaultValue = false);
float floatValueForKey(const char* key, float defaultValue = 0);
std::string stringValueForKey(const char* key);
GxxArray* arrayValueForKey(const char* key);
GxxDictionary* dictionaryValueForKey(const char* key);
GxxObject* otherValueForKey(const char* key);
void setValueForKey(GxxValuePtr& pValue, const char* key);
void setValueFromOtherKey(GxxDictionaryPtr& otherDir, const char* key);
void setValueFromOtherKey(GxxDictionaryPtr& otherDir, const char* key, const char* newKey);
void setValueForKey(int nValue, const char* key);
void setValueForKey(const char* szValue, const char* key);
void setValueForKey(const std::string& strValue, const char* key);
void setValueForKey(float fValue, const char* key);
void setValueForKey(GxxArrayPtr& arrValue, const char* key);
void setValueForKey(GxxDictionaryPtr& dirValue, const char* key);
/*
通过keypath 快速查找json中某个key下的值
例如如下举例:
GxxDictionaryPtr dr = GxxDictionary::create();
dr->initWithJsonText("{\"abc\":[1,2,3,{\"key1\":456,\"key2\":[4,5,6]}]}");
// 取出abc对应的value
GxxValuePtr pVal1 = dr->valueForKeyPath("abc");
if (pVal1) MY_TRACE(pVal1->describe().c_str());
// abc的value是一个数组,取出abc数组中的第二个元素, [下标从0开始]
GxxValuePtr pVal2 = dr->valueForKeyPath("abc[1]");
if (pVal2) MY_TRACE(pVal2->describe().c_str());
// abc的value是一个数组,取出abc数组中的第四个元素, [下标从0开始]
GxxValuePtr pVal3 = dr->valueForKeyPath("abc[3]");
if (pVal3) MY_TRACE(pVal3->describe().c_str());
// abc的value是一个数组,它的第四个元素又是一个字典, 取出字典里 key2对应的value
GxxValuePtr pVal4 = dr->valueForKeyPath("abc[3].key2");
if (pVal4) MY_TRACE(pVal4->describe().c_str());
// key2对应的value是一个数组,取出它的第一个元素
GxxValuePtr pVal5 = dr->valueForKeyPath("abc[3].key2[0]");
if (pVal5) MY_TRACE(pVal5->describe().c_str());
*/
GxxValue* valueForKeyPath(const char* keyPath);
/*
在指定的路径上设置value,这里的keyPath和 valueForKeyPath的keyPath参数有所不同,
前者的keyPath中不能包含[]下标.
GxxDictionaryPtr dr = GxxDictionary::create();
dr->initWithJsonText("{\"config\":{\"key1\":456,\"key2\":[4,5,6]}}");
// 在config下添加一个为key3的value, config下没有key3,setValueForKeyPath会自动创建key3
dr->setValueForKeyPath(GxxValue::create("my key is key3"),"config.key3");
// 重新设置key3的值, key3已存在,它的value被覆盖
dr->setValueForKeyPath(GxxValue::create("your key is key3"),"config.key3");
*/
void setValueForKeyPath(GxxValuePtr& pValue, const char* keyPath);
int intValueForKeyPath(const char* keyPath, int defaultValue = 0);
bool boolValueForKeyPath(const char* keyPath, bool defaultValue = false);
float floatValueForKeyPath(const char* keyPath, float defaultValue = 0);
std::string stringValueForKeyPath(const char* keyPath);
GxxArray* arrayValueForKeyPath(const char* keyPath);
GxxDictionary* dictionaryValueForKeyPath(const char* keyPath);
GxxObject* otherValueForKeyPath(const char* keyPath);
void removeKey(const char* key);
private:
private:
GxxValueMapPtr keyValues;
};
class G2X_CLASSES GxxArray : public GxxObject
{
GXX_CREATE_FUNC(GxxArray);
protected:
virtual void objectReleased();
public:
static GxxAutoPtr createWithObj(GxxValuePtr valPtr1,GxxValuePtr valPtr2=NULL,GxxValuePtr valPtr3=NULL);
static GxxAutoPtr createWithObj(GxxDictionaryPtr dicPtr1,GxxDictionaryPtr dicPtr2=NULL,GxxDictionaryPtr dicPtr3=NULL);
bool initWithJsonText(const char* jsonText, bool isUtf8=false);
std::string toJsonText(bool isUtf8);
virtual std::string describe(bool isUtf8=false);
virtual void print();
// 返回把数组转为json格式的字符串
std::string describe_d(int depth, bool bFormat, bool isUtf8=false);
/**
* 插入value在具体的某个位置
* @param pValue :
* @param iIndex : 插入的位置, =0表示插入在最前面,-1表示插入在最后面(等同于addValue)
* @see addValue
* @return void
*/
void insertValue(const GxxValuePtr& pValue, int iIndex);
/**
* 添加value在数组的最后面
* @param pValue :
* @return void
*/
void addValue(const GxxValuePtr& pValue);
void addValue(const GxxArrayPtr& arrVal);
void addValue(const GxxDictionaryPtr& dirVal);
void addValuesFrom(const GxxArrayPtr& otherArray);
void setValue(GxxValuePtr& pValue, int iIndex);
void removeValue(int iIndex);
void removeAllValues();
GxxValue* valueAtIndex(int iIndex);
int intValueAtIndex(int iIndex, int defaultValue = 0);
bool boolValueAtIndex(int iIndex, bool defaultValue = false);
std::string stringValueAtIndex(int iIndex);
GxxArray* arrayValueAtIndex(int iIndex);
GxxDictionary* dictionaryValueAtIndex(int iIndex);
GxxObject* otherValueAtIndex(int iIndex);
/**
* 使用数组中的字典元素的某个key的value做为新字典的key,新字典的key的value为 数组中的字典对象
* 例如: [{"A":"001","B":10000},{"A":"002","B":12000},{"A":"003","B":14000}], 这是一个公司3个员工的 员工编号和员工薪水记录数组
* 将它制造成一个字典,转为换: {"001":{"A":"001","B":10000},"002":{"A":"002","B":12000},"003":{"A":"003","B":14000}}
* 这样,读取某个员信息时,可以直接使用 员工编号,快速读取 员工信息了
* 当key-value重复时,后面的值覆盖前面的值
* @param useValueOfKey : 使用字典元素的某个key
* @return ...
*/
GxxDictionaryPtr makeDictionaryByKey(const char* useValueOfKey);
int count();
private:
typedef std::vector ArrayValue;
ArrayValue arrayValues;
};
class G2X_CLASSES GxxString : public GxxObject
{
GXX_CREATE_FUNC(GxxString);
public:
static GxxStringPtr create(const char* str);
virtual void print();
virtual std::string describe(bool isUtf8=false) { return _string; }
const char* getString();
private:
std::string _string;
};
class G2X_CLASSES GxxValue : public GxxObject
{
GXX_CREATE_FUNC(GxxValue);
public:
static GxxValuePtr create(const GxxDictionaryPtr& ptrValue);
static GxxValuePtr create(const GxxArrayPtr& ptrValue);
static GxxValuePtr create(GxxStringPtr& ptrValue);
static GxxValuePtr create(const char* pValue);
static GxxValuePtr create(const GxxObjectPtr& ptrOtherValue);
static GxxValuePtr create(unsigned long uValue);
static GxxValuePtr create(long intValue);
static GxxValuePtr create(long long int64Value);
static GxxValuePtr create(double floatValue);
virtual bool init(){
_init();
return true;
}
virtual void print();
virtual std::string describe();
GxxDictionary* getDictionary(){return ptrDictionaryData;}
GxxArray* getArray(){return ptrArrayData;}
GxxString* getString(){return ptrStringData;}
GxxObject* getOther(){return ptrOtherData;}
const char* stringValue();
int intValue();
float floatValue();
double doubleValue();
protected:
virtual void objectReleased();
private:
void _init();
private:
GxxDictionaryPtr ptrDictionaryData;
GxxArrayPtr ptrArrayData;
GxxStringPtr ptrStringData;
GxxObjectPtr ptrOtherData;
};
class G2X_CLASSES GxxValueMap : public GxxObject
{
GXX_CREATE_FUNC(GxxValueMap);
public:
struct Pair
{
GxxKey key;
GxxValuePtr value;
};
typedef std::map TyMap;
typedef TyMap::iterator Iterator;
int count();
void PutValue(const GxxKey& _key, const GxxValuePtr& _value);
GxxValuePtr ValueForKey(const GxxKey& _key);
void RemoveKey(const GxxKey& _key);
Iterator FirstValue();
bool IsLastValue(Iterator& it);
void NextValue(Iterator& pos);
protected:
virtual bool init();
virtual void objectReleased();
private:
TyMap* __pMap;
};
typedef void (GxxObject::*GxxNotifyHandleFunc)(void);
typedef void (GxxObject::*GxxNotifyHandleFuncWithData)(GxxDictionaryPtr dataPtr);
struct GxxNotification
{
GxxObject* observerPtr;
GxxNotifyHandleFunc func;
GxxNotifyHandleFuncWithData funcWithData;
};
class G2X_CLASSES GxxNotifyCenter : public GxxObject
{
GXX_CREATE_FUNC(GxxNotifyCenter);
public:;
typedef std::vector ArrayNofitication;
typedef ArrayNofitication::iterator ArrayIterator;
typedef std::map MapNotifications;
typedef MapNotifications::iterator MapIterator;
public:
static GxxNotifyCenter* defaultCenter();
void AddObserver(GxxObject* pObserverObj, const char* keyNotify, GxxNotifyHandleFunc handleFunc);
void AddObserver(GxxObject* pObserverObj, const char* keyNotify, GxxNotifyHandleFuncWithData handleFunc);
void RemoveObserver(GxxObject* pObserverObj, const char* keyNotify = 0);
void PostNotify(const char* keyNotify, GxxDictionary* data = 0);
private:
void _addObserver(const GxxNotification& noti, const char* keyNotify);
GxxNotification* _findObserver(GxxObject* pObserverObj, const char* keyNotify);
private:
MapNotifications mapNotifications;
};
#endif