Sleds/cppjson/jsonobject.cpp

404 lines
11 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright(c) 2015, 2016 Sequence Logic.
///////////////////////////////////////////////////////////////////////////////
#include "jsonobject.h"
#include <sstream>
#ifdef WIN32
#include <regex>
#endif
using namespace sequencelogic;
namespace
{
#ifdef WIN32
const std::regex JSON_ARRAY_PAT ("^([^\\[]+)\\[([\\d]+)\\](.*)");
#endif
int SplitString(const std::string &strToSplit, const char ch, std::vector<std::string> &splitStrings)
{
int nRetVal = 0;
splitStrings.clear();
std::string::size_type nStart = 0;
while (strToSplit.find(ch, nStart) != std::string::npos)
{
splitStrings.push_back(strToSplit.substr(nStart, strToSplit.find(ch, nStart) - nStart));
++nRetVal;
nStart = strToSplit.find(ch, nStart)+1;
}
// Check for a last arg...
if (nStart < strToSplit.length())
{
splitStrings.push_back(strToSplit.substr(nStart));
++nRetVal;
}
return nRetVal;
}
};
////////////////////////////////////////////////////////////////////////////////////
// JSONArray...
/**
* Does this array contain the passed in string?
*/
bool JSONArray::hasString(const std::string &str) const
{
//return (const_cast<JSONArray *>(this)->search(str.c_str()) != NULL);
bool bRetVal = false;
for (unsigned int i = 0; !bRetVal && (i < data.size()); ++i)
{
T_JSON *pObj = data[i];
if ((pObj != NULL) && (pObj->gettype() == J_STRING))
bRetVal = (str == pObj->getstring());
}
return bRetVal;
}
/**
* Remove an element from the array...
*/
void JSONArray::removeElement(const std::string &elem)
{
T_JSON *pVal = NULL;
for (T_JSON::IntData::iterator iter = data.begin(); iter != data.end(); ++iter)
{
if (::strcmp((*iter)->getstring(), elem.c_str()) == 0)
{
pVal = *iter;
data.erase(iter);
delete pVal;
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
// JSONObject...
// Load JSON from a file.
JSONObject::JSONObject(const std::string &jsonStr)
{
if (jsonStr != "")
loadFromString(jsonStr.c_str());
}
/**
* Make this into a JSON string.
*/
std::string JSONObject::toString(bool bPrettyPrint /*= false*/) const
{
std::stringstream outStr;
print(outStr, 4, bPrettyPrint);
std::string retVal = outStr.str();
// It seems that the name of the object/array tags along, if the object/array is embedded in another object.
if ((parent != NULL) && (retVal != "") &&
(retVal[0] != '{') && (retVal[0] != '['))
{
std::string::size_type nOpenIdx = (type == J_OBJECT) ? retVal.find('{') : retVal.find('[');
if (nOpenIdx > retVal.find(':'))
retVal.erase(0, retVal.find(':')+1);
}
return retVal;
}
bool JSONObject::LoadFromFile(const std::string &fileName)
{
return load(fileName.c_str());
}
// Save JSON to a file.
bool JSONObject::SaveToFile(const std::string &fileName, bool bPretty /*= true*/)
{
return store(fileName.c_str(), bPretty);
}
/*
* Create an empty array with the provided key. If an array already exists, it is not touched.
*/
void JSONObject::setJSONArray(const char *pKey)
{
if (search(pKey) == NULL)
{
T_JSON *pObj = new T_JSON(T_JSON::J_ARRAY);
pObj->setname(pKey);
add(pObj);
}
}
/*
* Set a JSON array.
*/
void JSONObject::setJSONValue(const char *pKey, const JSONArray &value)
{
if (search(pKey) != NULL)
erase(pKey);
T_JSON *pObj = new T_JSON(static_cast<const T_JSON &>(value));
pObj->setname(pKey);
add(pObj);
}
/*
* Set a JSON object.
*/
void JSONObject::setJSONValue(const char *pKey, const JSONObject &value)
{
if (search(pKey) != NULL)
erase(pKey);
T_JSON *pObj = new T_JSON(static_cast<const T_JSON &>(value));
pObj->setname(pKey);
add(pObj);
}
/*
* Get a JSON object from this one.
*/
JSONObject& JSONObject::getJSONObject(const char *pKey)
{
static JSONObject emptyObj;
emptyObj.free();
T_JSON *pTmpObj;
if (((pTmpObj = search(pKey)) != NULL) &&
((pTmpObj->gettype() == T_JSON::J_OBJECT) || (pTmpObj->gettype() == T_JSON::J_ARRAY)))
return reinterpret_cast<JSONObject &>(*pTmpObj);
else
return emptyObj;
}
/*
* Get a JSON array from this one.
*/
JSONArray& JSONObject::getJSONArray(const char *pKey)
{
static JSONArray emptyArray;
emptyArray.free();
T_JSON *pTmpObj;
if (((pTmpObj = search(pKey)) != NULL) && (pTmpObj->gettype() == T_JSON::J_ARRAY))
return reinterpret_cast<JSONArray &>(*pTmpObj);
else
return emptyArray;
}
/*
* Merge another JSON object with this one. Replace any data in this object with the other, if keys match.
*/
void JSONObject::mergeWith(const JSONObject &other)
{
for (unsigned int i = 0; i < other.data.size(); ++i)
{
T_JSON *pOther = other.data[i];
if (search(pOther->getname()) != NULL)
erase(pOther->getname());
add(new T_JSON(*pOther));
}
}
/*
* Get a string value from the JSON object.
*/
std::string JSONObject::getJSONString(const char *pKey, const std::string &altVal /*= ""*/) const
{
std::string retVal = altVal;
const T_JSON *pVal = NULL;
if (std::string(pKey).find('.') != std::string::npos)
{
std::vector<std::string> pathParts;
SplitString(pKey, '.', pathParts);
pVal = this;
for (unsigned int i = 0; (pVal != NULL) && (i < pathParts.size()); ++i)
{
T_JSON *pTmpMember = pVal->search(pathParts[i].c_str());
if (pTmpMember != NULL)
pVal = pTmpMember;
else
pVal = NULL;
}
}
else
pVal = search(pKey);
if (pVal != NULL)
{
if (pVal->gettype() == J_NUMBER)
{
std::stringstream tmpStr;
if (pVal->getint() == pVal->getdouble())
tmpStr << pVal->getint();
else
tmpStr << pVal->getdouble();
retVal = tmpStr.str();
}
else
retVal = pVal->getstring();
}
return retVal;
}
#ifdef WIN32
std::string JSONObject::getJSONStringPath(const char *pKey, const std::string &altVal /*= ""*/) const
{
std::string retVal = altVal;
T_JSON *pVal =
const_cast<JSONObject *>(this)->findValuePath(const_cast<JSONObject *>(this), pKey, false);
if (pVal != NULL)
retVal = pVal->getstring();
return retVal;
}
bool JSONObject::getJSONBoolPath(const char *pKey, bool bAltVal /*= true*/) const
{
bool retVal = bAltVal;
T_JSON *pVal =
const_cast<JSONObject *>(this)->findValuePath(const_cast<JSONObject *>(this), pKey, false);
if (pVal != NULL)
retVal = pVal->getboolean();
return retVal;
}
unsigned int JSONObject::getJSONUIntPath(const char *pKey, unsigned int nAltVal /*= 0*/) const
{
unsigned int retVal = nAltVal;
T_JSON *pVal =
const_cast<JSONObject *>(this)->findValuePath(const_cast<JSONObject *>(this), pKey, false);
if (pVal != NULL)
retVal = pVal->getint();
return retVal;
}
unsigned long long JSONObject::getJSONUInt64Path(const char *pKey, unsigned long long nAltVal /*= 0*/) const
{
unsigned long long retVal = nAltVal;
T_JSON *pVal =
const_cast<JSONObject *>(this)->findValuePath(const_cast<JSONObject *>(this), pKey, false);
if (pVal != NULL)
retVal = pVal->getint();
return retVal;
}
void JSONObject::setJSONValuePath(const char *pKey, const char *pValue)
{
T_JSON *pVal = findValuePath(this, pKey, true);
if (pVal != NULL)
pVal->setstring(pValue);
}
void JSONObject::setJSONValuePath(const char *pKey, bool bVal)
{
T_JSON *pVal = findValuePath(this, pKey, true);
if (pVal != NULL)
pVal->setboolean(bVal);
}
void JSONObject::setJSONValuePath(const char *pKey, unsigned long long nVal)
{
T_JSON *pVal = findValuePath(this, pKey, true);
if (pVal != NULL)
pVal->setnumber(static_cast<int64_t>(nVal));
}
#endif
namespace sequencelogic
{
/*std::ostream& operator<<(std::ostream &out, const boost::property_tree::ptree &obj)
{
boost::property_tree::json_parser::write_json(out, obj, false);
return out;
}*/
std::ostream& operator<<(std::ostream &out, const JSONObject &obj)
{
if (obj.empty())
out << "{}";
else
out << obj.toString();
return out;
}
std::ostream& operator<<(std::ostream &out, const JSONArray &obj)
{
if (obj.empty())
out << "[]";
else
out << obj.toString();
return out;
}
}
#ifdef WIN32
T_JSON *JSONObject::findValuePath(T_JSON *pStartVal, const char *pPath, bool bCreateIfNecessary)
{
T_JSON *pRetVal = NULL;
std::cmatch matchRes;
if (std::regex_match(pPath, matchRes, JSON_ARRAY_PAT))
{
std::string var;
int nIdx = 0;
std::string tail;
{
std::cmatch::const_iterator iter = matchRes.cbegin();
++iter;
var = *iter;
++iter;
std::stringstream tmpStr(*iter);
tmpStr >> nIdx;
++iter;
tail = *iter;
}
T_JSON *pTmpMember = findValuePath(pStartVal, var.c_str(), bCreateIfNecessary);
if (bCreateIfNecessary && (pTmpMember == NULL))
{
pTmpMember = new T_JSON(var.c_str(), T_JSON::J_ARRAY);
pStartVal->add(pTmpMember);
for (int i = 0; i < nIdx+1; ++i)
pTmpMember->add(new T_JSON());
}
else if (bCreateIfNecessary && (nIdx >= pTmpMember->getnumelements()))
{
pTmpMember->settype(T_JSON::J_ARRAY);
for (int i = nIdx; i <= nIdx+1; ++i)
pTmpMember->add(new T_JSON("", ""));
}
else if (pTmpMember == NULL)
pRetVal = NULL;
if ((pTmpMember != NULL) && (nIdx < pTmpMember->getnumelements()))
{
pRetVal = pTmpMember->get(nIdx);
if ((tail != "") && (tail[0] == '.'))
{
tail.erase(tail.begin());
if (bCreateIfNecessary && (pRetVal->gettype() != T_JSON::J_OBJECT))
pRetVal->settype(T_JSON::J_OBJECT);
pRetVal = findValuePath(pRetVal, tail.c_str(), bCreateIfNecessary);
}
}
}
else
{
std::vector<std::string> pathParts;
SplitString(pPath, '.', pathParts);
pRetVal = pStartVal;
for (unsigned int i = 0; (pRetVal != NULL) && (i < pathParts.size()); ++i)
{
T_JSON *pTmpMember = pRetVal->search(pathParts[i].c_str());
if (bCreateIfNecessary && (pTmpMember == NULL))
pRetVal->add(pTmpMember = new T_JSON(pathParts[i].c_str(), T_JSON::J_OBJECT));
if (pTmpMember != NULL)
pRetVal = pTmpMember;
else
pRetVal = NULL;
}
}
return pRetVal;
}
#endif