Sleds/cppjson/json.h

403 lines
13 KiB
C
Raw Normal View History

2025-03-13 21:28:38 +00:00
///////////////////////////////////////////////////////////////////////////////
// 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