403 lines
13 KiB
C
403 lines
13 KiB
C
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Copyright(c) 2015, 2016 Sequence Logic.
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
//
|
||
|
|
|
||
|
|
#ifndef __JSON__H__
|
||
|
|
#define __JSON__H__
|
||
|
|
|
||
|
|
#ifdef WIN32
|
||
|
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
||
|
|
#define _CRT_SECURE_NO_WARNINGS // Disable the warnings about 'sprintf' and 'strcpy'. The "XXX_s" versions haven't make it to Linux yet.
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <stdlib.h>
|
||
|
|
#if !defined(__APPLE__)
|
||
|
|
#include <malloc.h>
|
||
|
|
#endif
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
|
||
|
|
#include <vector>
|
||
|
|
#include <string>
|
||
|
|
#include <locale>
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
namespace sequencelogic
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* A wrapper to handle parsing a string or a FILE for JSON
|
||
|
|
*/
|
||
|
|
class JSONStream
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
JSONStream(FILE *pFile) : _pFile(pFile), _nIdx(-1), _nStrlen(-1) {}
|
||
|
|
JSONStream(const char *pString) : _pString(pString), _nIdx(0) { _nStrlen = ::strlen(_pString); }
|
||
|
|
~JSONStream() {}
|
||
|
|
|
||
|
|
char getChar()
|
||
|
|
{
|
||
|
|
if (_nIdx < 0)
|
||
|
|
return ::fgetc(_pFile);
|
||
|
|
else if (_nIdx < _nStrlen)
|
||
|
|
return (_pString[_nIdx++]);
|
||
|
|
else
|
||
|
|
return EOF;
|
||
|
|
}
|
||
|
|
void putChar(char c)
|
||
|
|
{
|
||
|
|
if (_nIdx < 0)
|
||
|
|
::ungetc(c, _pFile);
|
||
|
|
else
|
||
|
|
--_nIdx;
|
||
|
|
}
|
||
|
|
|
||
|
|
double getNum()
|
||
|
|
{
|
||
|
|
double retVal = 0.0;
|
||
|
|
if (_nIdx < 0)
|
||
|
|
#ifndef LINUX
|
||
|
|
fscanf_s(_pFile, "%lf ", &retVal);
|
||
|
|
#else
|
||
|
|
fscanf(_pFile, "%lf ", &retVal);
|
||
|
|
#endif
|
||
|
|
else
|
||
|
|
{
|
||
|
|
#ifndef LINUX
|
||
|
|
sscanf_s(&(_pString[_nIdx]), "%lf ", &retVal);
|
||
|
|
#else
|
||
|
|
sscanf(&(_pString[_nIdx]), "%lf ", &retVal);
|
||
|
|
#endif
|
||
|
|
std::locale loc;
|
||
|
|
while (std::isdigit(_pString[_nIdx], loc) || (_pString[_nIdx] == '.'))
|
||
|
|
++_nIdx;
|
||
|
|
}
|
||
|
|
return retVal;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
union
|
||
|
|
{
|
||
|
|
const char *_pString;
|
||
|
|
FILE *_pFile;
|
||
|
|
};
|
||
|
|
int _nIdx;
|
||
|
|
int _nStrlen;
|
||
|
|
};
|
||
|
|
|
||
|
|
class T_JSON
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
typedef enum { J_NUMBER, J_STRING, J_BOOLEAN, J_ARRAY, J_OBJECT, J_NULL}
|
||
|
|
JSONTYPE;
|
||
|
|
typedef enum { J_MAXSTRLEN = 65536 } J_DUMMY;
|
||
|
|
|
||
|
|
protected:
|
||
|
|
JSONTYPE type = J_NULL;
|
||
|
|
|
||
|
|
union
|
||
|
|
{
|
||
|
|
double number;
|
||
|
|
char* string;
|
||
|
|
bool boolean;
|
||
|
|
};
|
||
|
|
|
||
|
|
T_JSON* parent = nullptr;
|
||
|
|
|
||
|
|
char* name = nullptr;
|
||
|
|
|
||
|
|
typedef std::vector<T_JSON *> IntData;
|
||
|
|
IntData data;
|
||
|
|
|
||
|
|
protected:
|
||
|
|
void error(const char* errorstr) const;
|
||
|
|
void error(const char* errorstr, FILE* f) const;
|
||
|
|
|
||
|
|
void checktype(JSONTYPE t1, JSONTYPE t2 = J_NULL) const;
|
||
|
|
|
||
|
|
void free(); // free any memory based on type.
|
||
|
|
|
||
|
|
void copyfrom(const T_JSON& o);
|
||
|
|
|
||
|
|
static T_JSON nulljson;
|
||
|
|
|
||
|
|
public:
|
||
|
|
// build it
|
||
|
|
T_JSON(const char* name = NULL, T_JSON* parent = NULL);
|
||
|
|
T_JSON(const char* name, double d) : name(NULL) { parent = NULL; setname(name);
|
||
|
|
type = J_NUMBER; number = d; }
|
||
|
|
T_JSON(const char* name, int32_t i) : name(NULL) { parent = NULL; setname(name);
|
||
|
|
type = J_NUMBER; number = i; }
|
||
|
|
T_JSON(const char* name, bool b) : name(NULL) { parent = NULL; setname(name);
|
||
|
|
type = J_BOOLEAN; boolean = b; }
|
||
|
|
T_JSON(const char* name, const char* s) : name(NULL) { parent = NULL; setname(name);
|
||
|
|
type = J_NULL; setstring(s); }
|
||
|
|
T_JSON(const char* name, JSONTYPE t) : name(NULL) { parent = NULL; setname(name);
|
||
|
|
type = t; string = NULL; }
|
||
|
|
T_JSON(JSONTYPE t) : name(NULL) { parent = NULL; setname("");
|
||
|
|
type = t; string = NULL; }
|
||
|
|
T_JSON(const T_JSON& o) : type(J_NULL), name(NULL) { parent = NULL; copyfrom(o); }
|
||
|
|
T_JSON(const std::string& s) { loadFromString(s); }
|
||
|
|
|
||
|
|
virtual ~T_JSON();
|
||
|
|
|
||
|
|
void operator = (const T_JSON& o) { copyfrom(o); }
|
||
|
|
void operator =(const std::string& s){ loadFromString(s); }
|
||
|
|
void operator =(const char* s) { loadFromString(s); }
|
||
|
|
bool operator ==(const T_JSON& rhs) const;
|
||
|
|
bool operator !=(const T_JSON& rhs) const { return !(*this == rhs); }
|
||
|
|
|
||
|
|
void settype(JSONTYPE t);
|
||
|
|
void setnumber(double d);
|
||
|
|
void setnumber(const char *pKey, double d);
|
||
|
|
void setnumber(int64_t d);
|
||
|
|
void setnumber(const char *pKey, int64_t d);
|
||
|
|
void setstring(const char* s);
|
||
|
|
void setstring(const char *pKey, const char* s);
|
||
|
|
void setboolean(bool b);
|
||
|
|
void setboolean(const char *pKey, bool b);
|
||
|
|
void add(T_JSON* object);
|
||
|
|
void add(const T_JSON& object) { add(new T_JSON(object)); }
|
||
|
|
void setname(const char *pName);
|
||
|
|
int32_t getnumitems() const { return (int32_t)data.size(); }
|
||
|
|
T_JSON* search(const char *pNameToFind);
|
||
|
|
T_JSON* search(const char *pNameToFind) const { return const_cast<T_JSON *>(this)->search(pNameToFind); }
|
||
|
|
const char* getname() const { if (name == NULL) return ""; else return name; }
|
||
|
|
void erase(const char *pKey);
|
||
|
|
void erase(const std::string& key) { erase(key.c_str()); }
|
||
|
|
|
||
|
|
public:
|
||
|
|
// Access the data
|
||
|
|
bool isnull() const { return type == J_NULL; }
|
||
|
|
|
||
|
|
JSONTYPE gettype() const { return type; }
|
||
|
|
T_JSON* getparent() const { return parent; }
|
||
|
|
int32_t getnumelements() const { return getnumitems(); }
|
||
|
|
T_JSON* get(int32_t i) const; // can also use operator []
|
||
|
|
T_JSON* get(const char*) const; // can also use operator []
|
||
|
|
|
||
|
|
const char* getstring() const; // these access functions are inlined
|
||
|
|
double getdouble() const; // below.
|
||
|
|
int32_t getint() const;
|
||
|
|
bool getboolean() const;
|
||
|
|
|
||
|
|
// update value in object. Add if missing. Inline below.
|
||
|
|
void update(const char*, const T_JSON& newval);
|
||
|
|
void update(const std::string& name, const T_JSON& newval) { update(name.c_str(), newval); }
|
||
|
|
void update(const T_JSON& newval) { update(newval.name, newval); }
|
||
|
|
bool merge(const T_JSON& o); // Assumes this and o are J_OBJECT. Return false if they aren't.
|
||
|
|
// Merge from another JSON object, replacingany fields in this one.
|
||
|
|
|
||
|
|
operator const char*() const { return getstring(); }
|
||
|
|
operator double() const { return getdouble(); }
|
||
|
|
operator int32_t() const { return getint(); }
|
||
|
|
operator bool() const { return getboolean(); }
|
||
|
|
|
||
|
|
const T_JSON& operator[](int32_t i) const { return const_cast<T_JSON *>(this)->operator[](i); }
|
||
|
|
T_JSON& operator[](int32_t i);
|
||
|
|
const T_JSON& operator[](const char* s) const { return const_cast<T_JSON *>(this)->operator[](s); }
|
||
|
|
T_JSON& operator[](const char* s);
|
||
|
|
const T_JSON& operator[](const std::string& s) const
|
||
|
|
{ return (*this)[s.c_str()]; }
|
||
|
|
T_JSON& operator[](const std::string& s) { return (*this)[s.c_str()]; }
|
||
|
|
|
||
|
|
|
||
|
|
public:
|
||
|
|
// I/O
|
||
|
|
bool loadFromString(const char *pJSONStr);
|
||
|
|
bool loadFromString(const std::string& s) { return loadFromString(s.c_str()); }
|
||
|
|
bool load(const char* filename); // Load whole tree from a file
|
||
|
|
bool load(FILE* f); // Load this object.
|
||
|
|
bool load(JSONStream &stream);
|
||
|
|
//bool load(std::istream &inStr);
|
||
|
|
|
||
|
|
bool store(const char* filename, bool bPrettyPrint = true) const; // Save whole thing to a file
|
||
|
|
bool print(std::ostream &out, int indent = 4, bool bPrettyPrint = false) const; // Write this object to a file
|
||
|
|
|
||
|
|
std::string toString(int indent = 4, bool pretty = false) const; // generate a string.
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Good ol' Microsoft! They've added a constructor to the std::exception class that actually
|
||
|
|
* takes a const char *! However, this is *not* in the C++11 standard!
|
||
|
|
*/
|
||
|
|
class T_JSONException : public std::exception
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
T_JSONException(const char *pMsg) { _msg = pMsg; }
|
||
|
|
~T_JSONException() throw() {}
|
||
|
|
|
||
|
|
virtual const char *what() { return _msg.c_str(); }
|
||
|
|
protected:
|
||
|
|
std::string _msg;
|
||
|
|
};
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Inline functions
|
||
|
|
inline void T_JSON::checktype(JSONTYPE t1, JSONTYPE t2) const
|
||
|
|
{
|
||
|
|
if (type != t1 && (t2 == J_NULL || t2 != type))
|
||
|
|
error("Type mismatch");
|
||
|
|
}
|
||
|
|
|
||
|
|
inline const char* T_JSON::getstring() const
|
||
|
|
{
|
||
|
|
if (isnull())
|
||
|
|
return "";
|
||
|
|
checktype(J_STRING);
|
||
|
|
return string;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline double T_JSON::getdouble() const
|
||
|
|
{
|
||
|
|
if (type == J_BOOLEAN)
|
||
|
|
return boolean ? 1 : 0;
|
||
|
|
if (type == J_NUMBER)
|
||
|
|
return number;
|
||
|
|
if (type == J_STRING)
|
||
|
|
{
|
||
|
|
char* end = NULL;
|
||
|
|
double d = strtod(string, &end);
|
||
|
|
if (!*end)
|
||
|
|
return d;
|
||
|
|
}
|
||
|
|
checktype(J_NUMBER);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline int32_t T_JSON::getint() const
|
||
|
|
{
|
||
|
|
if (type == J_BOOLEAN)
|
||
|
|
return boolean ? 1 : 0;
|
||
|
|
if (type == J_NUMBER)
|
||
|
|
return static_cast<int32_t>(number);
|
||
|
|
if (type == J_STRING)
|
||
|
|
{
|
||
|
|
char* end = NULL;
|
||
|
|
double d = strtod(string, &end);
|
||
|
|
if (!*end)
|
||
|
|
return static_cast<int32_t>(d);
|
||
|
|
}
|
||
|
|
checktype(J_NUMBER);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool T_JSON::getboolean() const
|
||
|
|
{
|
||
|
|
if (isnull())
|
||
|
|
return false;
|
||
|
|
if (type == J_BOOLEAN)
|
||
|
|
return boolean;
|
||
|
|
if (type == J_NUMBER)
|
||
|
|
return number != 0;
|
||
|
|
if (type == J_STRING)
|
||
|
|
{
|
||
|
|
if (::strcmp(string,"true") == 0)
|
||
|
|
return true;
|
||
|
|
if (::strcmp(string,"false") == 0)
|
||
|
|
return false;
|
||
|
|
char* end = NULL;
|
||
|
|
double d = strtol(string, &end, 0);
|
||
|
|
if (!*end)
|
||
|
|
return d != 0;
|
||
|
|
}
|
||
|
|
checktype(J_BOOLEAN);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline T_JSON* T_JSON::get(int32_t i) const
|
||
|
|
{
|
||
|
|
if (type == J_ARRAY || type == J_OBJECT)
|
||
|
|
//return (dynamic_cast<std::vector<T_JSON *> *>(const_cast<T_JSON *>(this))->operator[](i));
|
||
|
|
return data[i];
|
||
|
|
|
||
|
|
error("Attempt to subscript JSON object that is not an array!");
|
||
|
|
throw T_JSONException("Attempt to subscript JSON object that is not an array!");
|
||
|
|
//exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline T_JSON& T_JSON::operator[](int32_t i)
|
||
|
|
{
|
||
|
|
T_JSON* j = get(i);
|
||
|
|
return *j;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline T_JSON* T_JSON::get(const char* s) const
|
||
|
|
{
|
||
|
|
if (type != J_OBJECT)
|
||
|
|
{
|
||
|
|
error("Attempt to use a string to index a non-object!");
|
||
|
|
throw T_JSONException("Attempt to use a string to index a non-object!");
|
||
|
|
//exit(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
return const_cast<T_JSON &>(*this).search(s);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline T_JSON& T_JSON::operator[](const char* s)
|
||
|
|
{
|
||
|
|
T_JSON* j = get(s);
|
||
|
|
return j ? *j : nulljson;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline void T_JSON::setnumber(const char *pKey, double d)
|
||
|
|
{
|
||
|
|
T_JSON *pObj = get(pKey);
|
||
|
|
if (pObj == NULL)
|
||
|
|
{
|
||
|
|
pObj = new T_JSON(pKey);
|
||
|
|
add(pObj);
|
||
|
|
}
|
||
|
|
pObj->setnumber(d);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline void T_JSON::setnumber(const char *pKey, int64_t i)
|
||
|
|
{
|
||
|
|
T_JSON *pObj = get(pKey);
|
||
|
|
if (pObj == NULL)
|
||
|
|
{
|
||
|
|
pObj = new T_JSON(pKey);
|
||
|
|
add(pObj);
|
||
|
|
}
|
||
|
|
pObj->setnumber(i);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline void T_JSON::setstring(const char *pKey, const char* s)
|
||
|
|
{
|
||
|
|
T_JSON *pObj = get(pKey);
|
||
|
|
if (pObj == NULL)
|
||
|
|
{
|
||
|
|
pObj = new T_JSON(pKey);
|
||
|
|
add(pObj);
|
||
|
|
}
|
||
|
|
pObj->setstring(s);
|
||
|
|
}
|
||
|
|
inline void T_JSON::setboolean(const char *pKey, bool b)
|
||
|
|
{
|
||
|
|
T_JSON *pObj = get(pKey);
|
||
|
|
if (pObj == NULL)
|
||
|
|
{
|
||
|
|
pObj = new T_JSON(pKey);
|
||
|
|
add(pObj);
|
||
|
|
}
|
||
|
|
pObj->setboolean(b);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline void T_JSON::update(const char* pKey, const T_JSON& newval)
|
||
|
|
{
|
||
|
|
if (gettype() != J_OBJECT)
|
||
|
|
return;
|
||
|
|
|
||
|
|
T_JSON* pObj = get(pKey);
|
||
|
|
if (pObj == NULL)
|
||
|
|
add(pObj = new T_JSON(newval));
|
||
|
|
else
|
||
|
|
pObj->copyfrom(newval);
|
||
|
|
pObj->setname(pKey);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|