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