今天小编给大家分享一下怎么用C++编写一个Json解析器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
之前用RapidJson来做json的解析,但是,RapidJson还是有麻烦的地方,虽然速度非常快,但是由于用了非常多的优化技巧,反而无法做到我想要的那种简便的访问方式。
比如,有这么一个字符串:
"{ "a":1000,"b":30000,"c":[123,456,789,5555, 1.0e2, true, false, null, "test", "big big world"]}"
我在C++里面需要非常简单的使用它,例如这样:
static char text[] = "{ "a":1000,"b":30000,"c":[123,456,789,5555, 1.0e2, true, false, null, "test", "big big world"]}"; atom::CJson root = text; root["a"] = 123; root["c"] = true; root["b"] = "b is the biggest"; atom::CJson test = "{"new key": 1037, 'test-key':1234e-5, 'array':[1,2,3,1,1,0] }"; root["e"] = test; test["array"][0] = 1000; atom::a_string value = root.Stringity(); printf( "%s ", value.c_str() );
而输出结果如下:
{"a":123, "b":"b is the biggest", "c":1, "e":{"new key":1037, "test-key":0.012340, "array":[1000, 2, 3, 1, 1, 0, ], }, }
找了几个Json库,似乎都没有我想要的那种效果。快的访问很麻烦,访问方便点的速度又上不去。后来还是决定自己写一个。
自己写出来后,测试了一下,在不开优化的情况下,时间开销大概是RapidJson的8倍,如果开编译器优化,则时间开销是RapidJson的4倍左右。其实还是有可以再优化的地方,但再优化就必须要损失易用性为代价。想了一下,还是放弃了,这个解析速度和访问的方便程度我已经很满意了。
而且我自己写的Json还能支持序列化到流数据,如果采用这个方式,恢复的速度和RapidJson的解析差不多。
tokenizer数据结构的头文件和cpp文件
#ifndef TAGJSONTOKEN_H #define TAGJSONTOKEN_H //Begin section for file tagJsonToken.h //TODO: Add definitions that you want preserved //End section for file tagJsonToken.h #include "../stl/a_string.h" #include "../stl/allocator.h" #include "../tool/CVariablePtr.h" namespace atom { //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" struct tagJsonToken { //Begin section for atom::tagJsonToken //TODO: Add attributes that you want preserved //End section for atom::tagJsonToken public: //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" //typedef CVariablePtr<tagJsonToken> Ptr ; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" typedef vector<tagJsonToken, atom_allocator<tagJsonToken> > Array ; public: //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" U32 token; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" size_t start; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" size_t close; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonToken(); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonToken(const tagJsonToken & value); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonToken(U32 token); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonToken(U32 token, size_t start, size_t close); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" ~tagJsonToken(); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonToken & operator=(const tagJsonToken & value); }; //end struct tagJsonToken } //end namespace nova #endif
#include "tagJsonToken.h" //Begin section for file tagJsonToken.cpp //TODO: Add definitions that you want preserved //End section for file tagJsonToken.cpp //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken::tagJsonToken() : token(0),start(0),close(0) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken::tagJsonToken(const tagJsonToken & in) : token(in.token),start(in.start),close(in.close) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken::tagJsonToken(U32 t) : token(t),start(0),close(0) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken::tagJsonToken(U32 t, size_t s, size_t c) : token(t),start(s),close(c) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken::~tagJsonToken() { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonToken & atom::tagJsonToken::operator=(const tagJsonToken & in) { //TODO Auto-generated method stub token = in.token; start = in.start; close = in.close; return( * this ); }
json节点的头文件和cpp文件
#ifndef TAGJSONKEYVALUE_H #define TAGJSONKEYVALUE_H //Begin section for file tagJsonKeyValue.h //TODO: Add definitions that you want preserved //End section for file tagJsonKeyValue.h #include "../stl/a_string.h" #include "../stl/stl_extend.h" #include "../variant/CVariant.h" #include "../tool/CVariablePtr.h" namespace atom { //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" struct tagJsonKeyValue { //Begin section for atom::tagJsonKeyValue //TODO: Add attributes that you want preserved //End section for atom::tagJsonKeyValue public: //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" typedef CVariablePtr<tagJsonKeyValue> Ptr ; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" typedef vector<pair<size_t, tagJsonKeyValue::Ptr>, atom_allocator<pair<size_t, tagJsonKeyValue::Ptr> > > Array ; public: //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" a_string index; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" CVariant value; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" Array group; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" //Map query; //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue(); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue(const char * value); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue(const CVariant & data); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue(const char * value, const CVariant & data); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue(const tagJsonKeyValue & value); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" ~tagJsonKeyValue(); //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" tagJsonKeyValue & operator=(const tagJsonKeyValue & value); }; //end struct tagJsonKeyValue } //end namespace atom template<class Archive> inline void Serialize(Archive & archive, atom::tagJsonKeyValue & value, bool isSave) { UNREFERENCED_PARAMETER( isSave ); archive.Bind( value.index ); archive.Bind( value.value ); archive.Bind( value.group ); } #endif
#include "tagJsonKeyValue.h" //Begin section for file tagJsonKeyValue.cpp //TODO: Add definitions that you want preserved //End section for file tagJsonKeyValue.cpp //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::tagJsonKeyValue() { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::tagJsonKeyValue(const char * in): index(in ? in : "") { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::tagJsonKeyValue(const CVariant & in) : value(in) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::tagJsonKeyValue(const char * in_1, const CVariant & in_2) : index(in_1 ? in_1 : ""),value(in_2) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::tagJsonKeyValue(const tagJsonKeyValue & in) : index(in.index),value(in.value),group(in.group) { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue::~tagJsonKeyValue() { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::tagJsonKeyValue & atom::tagJsonKeyValue::operator=(const tagJsonKeyValue & in) { //TODO Auto-generated method stub index = in.index; value = in.value; group = in.group; return( * this ); }
接下来是 Tokenizer 的实现
#include "CJsonTokenizer.h" #include "../../enumeration/JSON_TOKEN.h" //Begin section for file CJsonTokenizer.cpp //TODO: Add definitions that you want preserved //End section for file CJsonTokenizer.cpp //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::CJsonTokenizer::CJsonTokenizer() { //TODO Auto-generated method stub tokens.reserve( 1024 ); } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" atom::CJsonTokenizer::~CJsonTokenizer() { //TODO Auto-generated method stub } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::Start(const char * json) { //TODO Auto-generated method stub if( json == NULL ) { return false; } size_t offset = 0; size_t length = strlen( json ); buffer.Alloc( length ); if( buffer ) { buffer.Store( json, length ); } bool result = true; for( ;; ) { // skip any reserved or space characters. for( ; IsSpace(json, offset, length); ++ offset ); // offset check if( offset >= length ) { tokens.push_back( tagJsonToken() ); tokens.back().token = JT_END; break; } char c = json[offset]; // create token if( IsNull(json, offset, length) ) { offset += 4; tokens.push_back( tagJsonToken(JT_NULL) ); } else if( c == ',' ) { offset += 1; tokens.push_back( tagJsonToken(JT_COMMA) ); } else if( c == ':' ) { offset += 1; tokens.push_back( tagJsonToken(JT_COLON) ); } else if( c == '{' ) { offset += 1; tokens.push_back( tagJsonToken(JT_OBJECT_BEGIN) ); } else if( c == '[' ) { offset += 1; tokens.push_back( tagJsonToken(JT_ARRAY_BEGIN) ); } else if( c == ']' ) { offset += 1; tokens.push_back( tagJsonToken(JT_ARRAY_CLOSE) ); } else if( c == '}' ) { offset += 1; tokens.push_back( tagJsonToken(JT_OBJECT_CLOSE) ); } else if( IsTrue(json, offset, length) ) { offset += 4; tokens.push_back( tagJsonToken(JT_BOOL, 1, 0) ); } else if( IsFalse(json, offset, length) ) { offset += 5; tokens.push_back( tagJsonToken(JT_BOOL) ); } else if( c == ''' || c == '"' ) { // read string will modify the offset; size_t start(0), close(0); ReadString( json, offset, length, start, close ); if( start == 0 || close == 0 || close <= start ) { char msg[32]; sprintf( msg, "%zu", offset ); errmsg = "Failed read string from offset "; errmsg = errmsg + msg; tokens.clear(); result = false; break; } tokens.push_back( tagJsonToken(JT_STRING, start, close) ); } else if( IsNumber(json, offset, length) ) { size_t start(0), close(0); ReadNumber( json, offset, length, start, close ); // read number will modify the offset; if( start == 0 || close == 0 || close <= start ) { char msg[32]; sprintf( msg, "%zu", offset ); errmsg = "Failed read number from offset "; errmsg = errmsg + msg; tokens.clear(); result = false; break; } tokens.push_back( tagJsonToken(JT_NUMBER, start, close) ); } else { char msg[32]; sprintf( msg, "%zu", offset ); errmsg = "Invalid char at offset "; errmsg = errmsg + msg; tokens.clear(); result = false; break; } } return result; } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::IsNull(const char * json, size_t & offset, size_t length) { //TODO Auto-generated method stub bool result = false; if( json ) { // length must enough. if( (length - offset) + 1 >= 4 ) { const char * site = json + offset; if( *site == 'n' || *site == 'N' ) { ++ site; if( *site == 'u' || *site == 'U' ) { ++ site; if( *site == 'l' || *site == 'L' ) { ++ site; if( *site == 'l' || *site == 'L' ) { result = true; } } } } } } return result; } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::IsTrue(const char * json, size_t & offset, size_t length) { //TODO Auto-generated method stub bool result = false; if( json ) { // length must enough. if( (length - offset) + 1 >= 4 ) { const char * site = json + offset; if( *site == 't' || *site == 'T' ) { ++ site; if( *site == 'r' || *site == 'R' ) { ++ site; if( *site == 'u' || *site == 'U' ) { ++ site; if( *site == 'e' || *site == 'E' ) { result = true; } } } } } } return result; } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::IsFalse(const char * json, size_t & offset, size_t length) { //TODO Auto-generated method stub bool result = false; if( json ) { // length must enough. if( (length - offset) + 1 >= 5 ) { const char * site = json + offset; if( *site == 'f' || *site == 'F' ) { ++ site; if( *site == 'a' || *site == 'A' ) { ++ site; if( *site == 'l' || *site == 'L' ) { ++ site; if( *site == 's' || *site == 'S' ) { ++ site; if( *site == 'e' || *site == 'E' ) { result = true; } } } } } } } return result; } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::IsSpace(const char * json, size_t & offset, size_t length) { //TODO Auto-generated method stub if( !json ) { return false; } bool result = false; if( offset < length ) { if( json[offset] <= 0x20 ) { result = true; } } return result; } //@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)" bool atom::CJsonTokenizer::IsNumber(const char * json, size_t & offset, size_t length) { //TODO Auto-generated method stub if( !json ) { &