Create a gist now

Instantly share code, notes, and snippets.

MVJSON - my old lib for JSON reading / writing
/*
*
* Compact JSON format parsing lib (native cross-platform c++)
*
* Copyright (C) 2013 Victor Laskin (victor.laskin@gmail.com)
* Details: http://vitiy.info/?p=102
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "MVJSON.h"
namespace JSON {
MVJSONReader::MVJSONReader(const string & source) {
root = nullptr;
if (source == "") return;
if (source[0] == '{')
root = parse(source);
if (source[0] == '[')
root = parse("{\"root\":" + source + "}");
}
MVJSONReader::~MVJSONReader() {
if (root != NULL)
delete root;
}
MVJSONNode::~MVJSONNode() {
if (values.size() > 0)
for (int i = 0; i < values.size(); i++)
delete values.at(i);
}
MVJSONNode* MVJSONReader::parse(string text)
{
string s = trim(text);
if (s.length() < 2) return NULL;
// object
if ((s[0] == '{') && (s[s.length() - 1] == '}'))
{
// erase last and first symbols
s.erase(0, 1);
s.erase(s.length() - 1, 1);
vector<string> parts;
splitList(s, parts);
MVJSONNode* node = new MVJSONNode();
for (int i = 0; i < parts.size(); i++)
node->values.push_back(parseValue(parts.at(i), false));
return node;
}
return NULL;
}
MVJSONValue* MVJSONReader::parseValue(string text, bool hasNoName)
{
string key;
string s;
splitInHalf(text, ":", key, s);
key = trim(key);
s = trim(s);
if (key.length() > 2)
{
// strip "
key.erase(0, 1);
key.erase(key.length() - 1, 1);
}
if (hasNoName)
{
s = text;
key = "";
}
if (s == "false") // bool
return new MVJSONValue(key, false);
if (s == "true") // bool
return new MVJSONValue(key, true);
if (s == "null") // null
return new MVJSONValue(key, MVJSON_TYPE_NULL);
char first = s[0];
if (first == '"') // string
return new MVJSONValue(key, s.substr(1, s.length() - 2));
if (first == '{') // object
return new MVJSONValue(key, parse(s));
if (first == '[') // array
{
s.erase(0, 1);
s.erase(s.length() - 1, 1);
MVJSONValue* val = new MVJSONValue(key, MVJSON_TYPE_ARRAY);
if (s != "")
{
vector<string> parts;
splitList(s, parts);
for (int i = 0; i < parts.size(); i++)
val->arrayValue.push_back(parseValue(parts.at(i), true));
}
return val;
}
// else its number!
if (s.find(".") == string::npos)
return new MVJSONValue(key, s, stringToLongLong(s));
else
return new MVJSONValue(key, s, stringToDouble(s));
return NULL;
}
MVJSONValue::~MVJSONValue()
{
if (objValue != NULL)
delete objValue;
if (arrayValue.size() > 0)
for (int i = 0; i < arrayValue.size(); i++)
delete arrayValue.at(i);
}
void MVJSONValue::init(MVJSON_TYPE valueType)
{
this->valueType = valueType;
objValue = NULL;
name = "";
}
MVJSONValue::MVJSONValue(const string& name, MVJSON_TYPE valueType)
{
init(valueType);
this->name = name;
}
MVJSONValue::MVJSONValue(const string& name, bool value)
{
init(MVJSON_TYPE_BOOL);
this->name = name;
boolValue = value;
}
MVJSONValue::MVJSONValue(const string& name, const string& value)
{
init(MVJSON_TYPE_STRING);
this->name = name;
stringValue = value;
// here we switch back special chars
// \" \\ \/ \b \f \n \r \t \u four-hex-digits
replace(stringValue, "\\\"", "\"");
replace(stringValue, "\\\\", "\\");
replace(stringValue, "\\/", "/");
replace(stringValue, "\\b", "\b");
replace(stringValue, "\\f", "\f");
replace(stringValue, "\\n", "\n");
replace(stringValue, "\\r", "\r");
replace(stringValue, "\\t", "\t");
// TODO - \u four-hex-digits
// SS::replace(stringValue, "\\\\", "\\");
}
MVJSONValue::MVJSONValue(const string& name, const string& source, long long value)
{
init(MVJSON_TYPE_INT);
this->name = name;
stringValue = source;
intValue = value;
}
MVJSONValue::MVJSONValue(const string& name, const string& source, double value)
{
init(MVJSON_TYPE_DOUBLE);
this->name = name;
stringValue = source;
doubleValue = value;
}
MVJSONValue::MVJSONValue(const string& name, MVJSONNode* value)
{
init(MVJSON_TYPE_OBJECT);
this->name = name;
objValue = value;
}
bool MVJSONNode::hasField(const string& name)
{
if (values.size() == 0) return false;
for (int i = 0; i < values.size(); i++)
if (values.at(i)->name == name)
return true;
return false;
}
MVJSONValue* MVJSONNode::getField(const string& name)
{
if (values.size() == 0) return NULL;
for (int i = 0; i < values.size(); i++)
if (values.at(i)->name == name)
return values.at(i);
return NULL;
}
MVJSONValue* MVJSONValue::field(const string& name) {
return objValue->getField(name);
}
double MVJSONNode::getFieldDouble(const string& name)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
if (value->valueType == MVJSON_TYPE_INT) return value->intValue;
if (value->valueType == MVJSON_TYPE_DOUBLE) return value->doubleValue;
return 0;
}
int MVJSONNode::getFieldInt(const string& name)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
if (value->valueType == MVJSON_TYPE_INT) return (int)value->intValue;
return 0;
}
long long MVJSONNode::getFieldLongLong(const string& name)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
if (value->valueType == MVJSON_TYPE_INT) return value->intValue;
return 0;
}
string MVJSONNode::getFieldString(const string& name)
{
MVJSONValue* value = getField(name);
if (value == NULL) return "";
if (value->valueType == MVJSON_TYPE_STRING) return value->stringValue;
if (value->valueType == MVJSON_TYPE_DOUBLE) return value->stringValue;
if (value->valueType == MVJSON_TYPE_INT) return value->stringValue;
return "";
}
bool MVJSONNode::getFieldBool(const string& name)
{
MVJSONValue* value = getField(name);
if (value == NULL) return false;
if (value->valueType == MVJSON_TYPE_INT) return (bool)value->intValue;
if (value->valueType == MVJSON_TYPE_BOOL) return value->boolValue;
return false;
}
double MVJSONValue::getFieldDouble(const string& name)
{
if (objValue == NULL) return 0;
return objValue->getFieldDouble(name);
}
int MVJSONValue::getFieldInt(const string& name)
{
if (objValue == NULL) return 0;
return objValue->getFieldInt(name);
}
long long MVJSONValue::getFieldLongLong(const string& name)
{
if (objValue == NULL) return 0;
return objValue->getFieldLongLong(name);
}
string MVJSONValue::getFieldString(const string& name)
{
if (objValue == NULL) return "";
return objValue->getFieldString(name);
}
bool MVJSONValue::getFieldBool(const string& name)
{
if (objValue == NULL) return false;
return objValue->getFieldBool(name);
}
// -------------------- writer -------------------------->
MVJSONWriter::MVJSONWriter()
{
result = "";
depth = 0;
counts.resize(50);
std::fill(counts.begin(), counts.end(), 0);
}
} /* namespace F2 */
/*
*
* Compact JSON format parsing lib (native cross-platform c++)
*
* Copyright (C) 2013 Victor Laskin (victor.laskin@gmail.com)
* Details: http://vitiy.info/compact-lib-for-json-parsing-cross-platform-c/
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. The names of the authors may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string>
#include <vector>
#include <sstream>
#include <stdlib.h>
#ifndef MVJSON_H_
#define MVJSON_H_
using namespace std;
namespace JSON {
/// JSON node types
enum MVJSON_TYPE
{
MVJSON_TYPE_NULL,
MVJSON_TYPE_OBJECT,
MVJSON_TYPE_ARRAY,
MVJSON_TYPE_STRING,
MVJSON_TYPE_INT,
MVJSON_TYPE_DOUBLE,
MVJSON_TYPE_BOOL
};
class MVJSONUtils {
protected:
// string parsing functions
inline static long long stringToLongLong(const string & s); ///< convert string to long long
inline static double stringToDouble(const string & s); ///< convert string to float
inline static bool symbolToBeTrimmed(const char& c); ///< check if symbol is space, tab or new line break
inline static string trim(const string & text); ///< trim spaces, tabs and line break
inline static void replace(string & target, const string & oldStr, const string & newStr); ///< replace all occurrences of substring
inline static void splitInHalf(const string & s, const string & separator, string & begin, string & end); ///< second half (output)
inline static void splitList(const string & s, vector<string> & parts);
};
class MVJSONNode;
/// JSON Value
class MVJSONValue : public MVJSONUtils {
public:
MVJSONValue(const string& name, MVJSON_TYPE valueType);
MVJSONValue(const string& name, bool value);
MVJSONValue(const string& name, const string& value);
MVJSONValue(const string& name, const string& source, long long value);
MVJSONValue(const string& name, const string& source, double value);
MVJSONValue(const string& name, MVJSONNode* value);
virtual ~MVJSONValue();
string name; ///< value name [optional]
MVJSON_TYPE valueType; ///< type of node
string stringValue; ///< value if data has string type
bool boolValue; ///< value if data has bool type
long long intValue; ///< value if data has int type
double doubleValue; ///< value if data has double type
MVJSONNode* objValue; ///< value if data has object type
vector<MVJSONValue*> arrayValue; ///< array of values
MVJSONValue* field(const string& name);
double getFieldDouble(const string& name); ///< get value of double field of VALUE OBJECT (objValue)
int getFieldInt(const string& name); ///< get value of int field of VALUE OBJECT (objValue)
string getFieldString(const string& name); ///< get value of string field of VALUE OBJECT (objValue)
bool getFieldBool(const string& name); ///< get value of bool field of VALUE OBJECT (objValue)
long long getFieldLongLong(const string& name); ///< get long long value from field
inline MVJSONValue* at(unsigned int i){ return arrayValue.at(i); }
inline int size() { if (valueType == MVJSON_TYPE_ARRAY) return arrayValue.size(); else return 1; }
private:
void init(MVJSON_TYPE valueType);
};
template <typename T> struct is_vector { static const bool value = false; };
template <typename T> struct is_vector< std::vector<T> > { static const bool value = true; };
template <typename T> struct is_vector< const std::vector<T> > { static const bool value = true; };
/// JSON Node (Object)
class MVJSONNode {
public:
virtual ~MVJSONNode();
vector<MVJSONValue*> values; ///< values (props)
bool hasField(const string& name); ///< check that object has field
MVJSONValue* getField(const string& name); ///< get field by name
template<class T>
inline T getValue(const string& name, typename enable_if<!is_vector<T>::value, T>::type* = nullptr); ///< get value of specified type
template<class T>
inline T getValue(const string& name, typename enable_if<is_vector<T>::value, T>::type* = nullptr); ///< get value of specified type
double getFieldDouble(const string& name); ///< get value of double field
int getFieldInt(const string& name); ///< get value of int field
long long getFieldLongLong(const string& name); ///< get value of int field
string getFieldString(const string& name); ///< get value of string field
bool getFieldBool(const string& name); ///< get value of bool field
};
/// Compact JSON parser (based on specification: http://www.json.org/)
class MVJSONReader : public MVJSONUtils {
public:
MVJSONReader(const string & source); ///< constructor from json source
virtual ~MVJSONReader();
MVJSONNode* root; ///< root object (if its null - parsing was failed)
private:
MVJSONValue* parseValue(string text, bool hasNoName); ///< parse value
MVJSONNode* parse(string text); ///< parse node
};
/// Simple writer into JSON format
class MVJSONWriter : public MVJSONUtils {
public:
MVJSONWriter();
string result;
inline MVJSONWriter& begin();
inline MVJSONWriter& begin(const string& name);
inline MVJSONWriter& end();
inline MVJSONWriter& beginList(const string& name);
inline MVJSONWriter& endList();
template< typename T >
inline MVJSONWriter& add(const string& name, const T& value);
template< typename T >
inline MVJSONWriter& add(const T& value);
inline MVJSONWriter& include(const string& json);
private:
inline void addComma();
vector<int> counts;
int depth;
template< typename T >
inline string toString(const T& value);
template< typename T >
inline string toString(const vector<T>& value);
};
// --------------- reader ------------------------------------->
template<class T>
//inline typename enable_if<!is_vector<T>::value, T>::type
inline T
MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<T>::value, T>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return "";
return value->stringValue;
}
template<class T>
//inline typename enable_if<is_vector<T>::value, T>::type
inline T
MVJSONNode::getValue(const string& name, typename enable_if<is_vector<T>::value, T>::type*)
{
typename std::remove_const<T>::type result;
MVJSONValue* value = getField(name);
if (value == NULL) return result;
for (auto item : value->arrayValue)
{
result.push_back(remove_pointer<decltype(std::declval<T>().at(0).get())>::type::fromJSON(item->objValue));
}
return result;
}
template<>
//inline typename enable_if<is_vector<T>::value, T>::type
inline const vector<int>
MVJSONNode::getValue(const string& name, typename enable_if<is_vector<const vector<int>>::value, const vector<int>>::type*)
{
vector<int> result;
MVJSONValue* value = getField(name);
if (value == NULL) return result;
for (auto item : value->arrayValue)
{
result.push_back((int)item->intValue);
}
return result;
}
template<>
inline const long long MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const long long>::value, const long long>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
return value->intValue;
}
template<>
inline const long MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const long>::value, const long>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
return (long)value->intValue;
}
template<>
inline const int MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const int>::value, const int>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0;
return (int)value->intValue;
}
template<>
inline const bool MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const bool>::value, const bool>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return false;
return value->boolValue;
}
template<>
inline const double MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const double>::value, const double>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0.0;
return value->doubleValue;
}
template<>
inline const float MVJSONNode::getValue(const string& name, typename enable_if<!is_vector<const float>::value, const float>::type*)
{
MVJSONValue* value = getField(name);
if (value == NULL) return 0.0;
return value->doubleValue;
}
// ------------------- inlined string processing functions ------------->
inline long long MVJSONUtils::stringToLongLong(const string & s)
{
return atoll(s.c_str());
}
inline double MVJSONUtils::stringToDouble(const string & s)
{
return atof(s.c_str());
}
inline bool MVJSONUtils::symbolToBeTrimmed(const char& c ///< the char to test
)
{
if (c == ' ') return true;
if (c == '\n') return true;
if (c == '\r') return true;
if (c == '\t') return true;
return false;
}
inline string MVJSONUtils::trim(const string & text ///< the text to trim
)
{
if (text.length() == 0) return "";
int start = 0;
while ((start < text.length()) && (symbolToBeTrimmed(text[start]))) start++;
int end = text.length() - 1;
while ((end > 0) && (symbolToBeTrimmed(text[end]))) end--;
if (start > end) return "";
return text.substr(start, end - start + 1);
}
inline void MVJSONUtils::splitInHalf(const string & s, ///< source string to split up
const string & separator, ///< separator string)
string & begin, ///< first part (output)
string & end ///< second half (output)
)
{
int pos = s.find(separator);
if (pos == string::npos) { begin = s; end = ""; return; }
begin = s.substr(0, pos);
end = s.substr(pos + separator.length(), s.length() - pos - separator.length());
}
/// split string by "," - ignore content inside of "{", "}", "[", "]" and quotations "...."
/// also take \" into account
/// (Code should be cleared of comments beforehand)
inline void MVJSONUtils::splitList(const string & s, ///< string to be splitted
vector<string> & parts ///< result parts
)
{
bool isNotInQuotes = true;
int b1 = 0;
int b2 = 0;
int lastPos = 0;
const char* start = s.c_str();
const char* ps = start;
while (*ps) // *ps != 0
{
if ((*ps == ',') && (isNotInQuotes) && (b1 == 0) && (b2 == 0))
{
parts.push_back(s.substr(lastPos, ps - start - lastPos));
lastPos = ps - start + 1;
}
if (isNotInQuotes)
{
if (*ps == '{') b1++;
if (*ps == '}') b1--;
if (*ps == '[') b2++;
if (*ps == ']') b2--;
}
if (*ps == '"')
{
isNotInQuotes = !isNotInQuotes;
if (ps != start)
if (*(ps-1) == '\\')
isNotInQuotes = !isNotInQuotes;
}
ps++;
}
parts.push_back(s.substr(lastPos, s.length() - lastPos));
}
inline void MVJSONUtils::replace(string & target, ///< text to be modified
const string & oldStr, ///< old string
const string & newStr ///< new string
)
{
unsigned int pos = 0;
unsigned int oldLen = oldStr.length();
unsigned int newLen = newStr.length();
for (;;)
{
pos = target.find(oldStr, pos);
if (pos == string::npos) break;
target.replace(pos, oldLen, newStr);
pos += newLen;
}
}
// ------------------------ writer ----------------------->
inline void MVJSONWriter::addComma()
{
if (depth < 0) return;
if (counts[depth] > 0) result += ",";
}
inline MVJSONWriter& MVJSONWriter::begin()
{
addComma();
result += "{";
counts[depth]++;
depth++;
counts[depth] = 0;
return *this;
}
inline MVJSONWriter& MVJSONWriter::begin(const string& name)
{
addComma();
result += "\"" + name + "\":{";
counts[depth]++;
depth++;
counts[depth] = 0;
return *this;
}
inline MVJSONWriter& MVJSONWriter::end()
{
result += "}";
depth--;
return *this;
}
template< typename T >
inline MVJSONWriter& MVJSONWriter::add(const string& name, const T& value)
{
addComma();
result += "\"" + name + "\":" + toString(value);
counts[depth]++;
return *this;
}
template< typename T >
inline MVJSONWriter& MVJSONWriter::add(const T& value)
{
addComma();
result += toString(value);
counts[depth]++;
return *this;
}
inline MVJSONWriter& MVJSONWriter::include(const string& json)
{
addComma();
result += json;
counts[depth]++;
return *this;
}
inline MVJSONWriter& MVJSONWriter::beginList(const string& name)
{
addComma();
result += "\"" + name + "\":[";
counts[depth]++;
depth++;
counts[depth] = 0;
return *this;
}
inline MVJSONWriter& MVJSONWriter::endList()
{
result += "]";
depth--;
return *this;
}
template< typename T >
inline string MVJSONWriter::toString(const T& value)
{
// default overload is for string-like types!
return "\"" + value + "\"";
/*
std::stringstream ss;
//ss.precision(6);
ss << value;
return ss.str();
*/
}
template< typename T >
inline string MVJSONWriter::toString(const vector<T>& value)
{
string result = "[";
for (auto item : value)
result += ((result != "[") ? "," : "") + item->toJSON();
result += "]";
return result;
}
template<>
inline string MVJSONWriter::toString(const vector<int>& value)
{
string result = "[";
for (auto item : value)
result += ((result != "[") ? "," : "") + std::to_string(item);
result += "]";
return result;
}
template<>
inline string MVJSONWriter::toString(const int& value)
{
return std::to_string(value);
}
template<>
inline string MVJSONWriter::toString(const double& value)
{
std::ostringstream os ;
os.precision(10);
os << value ;
return os.str() ;
}
template<>
inline string MVJSONWriter::toString(const float& value)
{
return std::to_string(value);
}
template<>
inline string MVJSONWriter::toString(const long long& value)
{
return std::to_string(value);
}
template<>
inline string MVJSONWriter::toString(const bool& value)
{
return value ? "true" : "false";
}
#if defined(OS_LINUX) || defined(F2_EMSCRIPTEN)
template<>
inline string MVJSONWriter::toString(const time_t& value)
{
return std::to_string(value);
}
#endif
template<>
inline string MVJSONWriter::toString(const string& value)
{
return "\"" + value + "\"";
}
template<>
inline string MVJSONWriter::toString(const char* const & value)
{
return "\"" + string(value) + "\"";
}
} /* namespace F2 */
#endif /* MVJSON_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment