Created
June 3, 2018 08:59
-
-
Save gabonator/85dac97fa56a2458bfdf824d71d42b3b to your computer and use it in GitHub Desktop.
light json parser for microcontrollers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CSubstring | |
{ | |
const char* mpString; | |
int mnBegin; | |
int mnLength; | |
public: | |
CSubstring(const char* pString) | |
{ | |
mpString = pString; | |
mnBegin = 0; | |
mnLength = (int)strlen(mpString); | |
} | |
CSubstring(const CSubstring& begin, const CSubstring& end) | |
{ | |
mpString = begin.mpString; | |
mnBegin = begin.mnBegin; | |
mnLength = end.mnBegin - mnBegin; | |
} | |
CSubstring() | |
{ | |
mpString = nullptr; | |
mnLength = 0; | |
} | |
char operator[](int n) const | |
{ | |
assert(mpString && n <= mnLength); | |
return mpString[mnBegin + n]; | |
} | |
int Length() const | |
{ | |
return mnLength; | |
} | |
int Find(char c) const | |
{ | |
for (int i=mnBegin; i<mnBegin+mnLength; i++) | |
if (mpString[i] == c) | |
return i - mnBegin; | |
return -1; | |
} | |
bool BeginsWith(const char* str) const | |
{ | |
return strncmp(mpString+mnBegin, str, strlen(str)) == 0; | |
} | |
bool operator ==(const char* str) const | |
{ | |
if (strlen(str) != mnLength) | |
return false; | |
return strncmp(mpString+mnBegin, str, mnLength) == 0; | |
} | |
void operator ++(int) | |
{ | |
mnBegin++; | |
mnLength--; | |
assert(mnLength >= 0); | |
} | |
void operator +=(int n) | |
{ | |
mnBegin += n; | |
mnLength -= n; | |
assert(mnLength >= 0); | |
} | |
CSubstring TakeFirst(int n) | |
{ | |
if (n<=0) | |
return CSubstring(); | |
CSubstring aux = *this; | |
aux.mnLength = n; | |
*this += n; | |
return aux; | |
} | |
const char* GetBuffer() const | |
{ | |
return mpString + mnBegin; | |
} | |
operator bool() const | |
{ | |
return mpString; | |
} | |
std::string ToString() const | |
{ | |
if (!mpString || mnLength == 0) | |
return ""; | |
return std::string(mpString + mnBegin, mnLength); | |
} | |
}; | |
class CJson | |
{ | |
enum { | |
MaxStringLength = 32 | |
}; | |
CSubstring mString; | |
public: | |
typedef void (* TKeyValueCallback)(const CSubstring&, const CSubstring&); | |
typedef void (* TValueCallback)(const CSubstring&); | |
public: | |
std::string ToString() const | |
{ | |
return mString.ToString(); | |
} | |
CJson() | |
{ | |
} | |
CJson(const CSubstring& str) : mString(str) | |
{ | |
} | |
char* GetString() | |
{ | |
static char strValue[MaxStringLength]; | |
strValue[0] = 0; | |
CSubstring copyString = mString; | |
TraverseString(copyString, [](const CSubstring& s) | |
{ | |
if (s.Length() < MaxStringLength-1) | |
{ | |
memcpy(strValue, s.GetBuffer(), s.Length()); | |
strValue[s.Length()] = 0; | |
} | |
}); | |
return strValue; | |
} | |
int GetNumber() | |
{ | |
static int nResult; | |
CSubstring copyString = mString; | |
nResult = 0; | |
TraverseNumber(copyString, [](const CSubstring& s) | |
{ | |
nResult = atoi(s.GetBuffer()); | |
}); | |
return nResult; | |
} | |
CJson operator[](const char* pKey) | |
{ | |
static CJson _aux; | |
static const char* _pKey; | |
_aux = CJson(); | |
_pKey = pKey; | |
CSubstring copyString = mString; | |
TraverseObject(copyString, [](const CSubstring& key, const CSubstring& value) | |
{ | |
if (key == _pKey) | |
_aux = CJson(value); | |
}); | |
return _aux; | |
} | |
CJson operator[](int nIndex) | |
{ | |
static CJson _aux; | |
static int _nIndex; | |
_aux = CJson(); | |
_nIndex = nIndex; | |
CSubstring copyString = mString; | |
TraverseArray(copyString, [](const CSubstring& value) | |
{ | |
if (_nIndex-- == 0) | |
_aux = CJson(value); | |
}); | |
return _aux; | |
} | |
operator bool() const | |
{ | |
return mString; | |
} | |
bool Verify() | |
{ | |
CSubstring copyString = mString; | |
return TraverseAny(copyString); | |
} | |
private: | |
bool TraverseString(CSubstring& s, TValueCallback callback) | |
{ | |
if (!s) | |
return false; | |
char term = s[0]; | |
if (term != '\"' && term != '\'') | |
return false; | |
s++; | |
for (int i=0; i<s.Length(); i++) | |
{ | |
if (s[i] == '\\') | |
{ | |
i++; | |
continue; | |
} | |
if (s[i] == term) | |
{ | |
callback(s.TakeFirst(i)); | |
s++; | |
break; | |
} | |
} | |
return true; | |
} | |
bool TraverseNumber(CSubstring& s, TValueCallback callback) | |
{ | |
if (!s) | |
return false; | |
if (s[0] == '+' || s[0] == '-' || s[0] == '.' || (s[0] >= '0' && s[0] <= '9') ) | |
{ | |
int i; | |
for (i=1; i<s.Length(); i++) | |
if (s[i] != '.' && (s[i] < '0' || s[i] > '9') ) | |
break; | |
callback(s.TakeFirst(i)); | |
return true; | |
} | |
return false; | |
} | |
bool TraverseKeyword(CSubstring& s, TValueCallback callback) | |
{ | |
if (!s) | |
return false; | |
if (s.BeginsWith("null")) | |
{ | |
callback(s.TakeFirst(4)); | |
return true; | |
} | |
if (s.BeginsWith("true")) | |
{ | |
callback(s.TakeFirst(4)); | |
return true; | |
} | |
if (s.BeginsWith("false")) | |
{ | |
callback(s.TakeFirst(5)); | |
return true; | |
} | |
return false; | |
} | |
bool TraverseKey(CSubstring& s, TValueCallback callback) | |
{ | |
if (!s) | |
return false; | |
if (TraverseString(s, callback)) | |
return true; | |
int nEnd = s.Find(':'); | |
if (nEnd < 0) | |
return false; | |
callback(s.TakeFirst(nEnd)); | |
return true; | |
} | |
bool TraverseAny(CSubstring& s) | |
{ | |
if (TraverseKeyword(s, [](const CSubstring&){})) | |
return true; | |
if (TraverseNumber(s, [](const CSubstring&){})) | |
return true; | |
if (TraverseString(s, [](const CSubstring&){})) | |
return true; | |
if (TraverseObject(s, [](const CSubstring&, const CSubstring&){})) | |
return true; | |
if (TraverseArray(s, [](const CSubstring&){})) | |
return true; | |
return false; | |
} | |
bool TraverseObject(CSubstring& s, TKeyValueCallback fCallback) | |
{ | |
if (!s || s[0] != '{') | |
return false; | |
s++; | |
while (s.Length() && s[0] != '}') | |
{ | |
if (s[0] == ' ' || s[0] == ',') | |
{ | |
s++; | |
continue; | |
} | |
static CSubstring _key; | |
TraverseKey(s, [](const CSubstring& sub){ _key = sub; }); | |
CSubstring key = _key; | |
if (s[0] != ':') | |
{ | |
//assert(0); | |
return false; | |
} | |
s++; | |
CSubstring valueBegin(s); | |
if (!TraverseAny(s)) | |
{ | |
//assert(0); | |
return false; | |
} | |
CSubstring value(valueBegin, s); | |
fCallback(key, value); | |
} | |
s++; | |
return true; | |
} | |
bool TraverseArray(CSubstring& s, TValueCallback fCallback) | |
{ | |
if (!s || s[0] != '[') | |
return false; | |
s++; | |
while (s.Length() && s[0] != ']') | |
{ | |
if (s[0] == ' ' || s[0] == ',') | |
{ | |
s++; | |
continue; | |
} | |
CSubstring valueBegin(s); | |
if (!TraverseAny(s)) | |
{ | |
assert(0); | |
return false; | |
} | |
CSubstring value(valueBegin, s); | |
fCallback(value); | |
} | |
s++; | |
return true; | |
} | |
}; | |
void jsonTest() | |
{ | |
const char* pJson = R"-({"ts":1527979991, "time":"2018-06-03 00:53:11", "ip":"37.9.169.18", "json":{"uid":"891468008914", "reset":"2 - NO_MEAN", "n":3771, "uptime":124779, "delayed":0, "temp":30.3, "hum":46.0, "cashless":{"state":0,"coin":{"full":1,"5":"66","10":"43","20":"47","50":"43","100":"44","200":"38"},"sell":{"9":1}}, "failed":42, "restarts":85, "rssi":15, "rx":1205356, "tx":1849411}, "agent":"iot-endpoint-valky-2018-1 (sim900 on esp8266 by valky.eu built May 24 2018 19:14:00)"})-"; | |
CJson json(pJson); | |
std::cout << json.Verify() << "\n"; | |
std::cout << "time=" << json["time"].GetString() << "\n"; | |
std::cout << "50coins=" << json["json"]["cashless"]["coin"]["50"].GetString() << "\n"; | |
std::cout << "sell=" << json["json"]["cashless"]["sell"]["9"].GetNumber() << "\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment