740 lines
19 KiB
C++
740 lines
19 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright(c) 2015, 2016 Sequence Logic.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
#include "json.h"
|
|
|
|
//#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <locale>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
|
|
using namespace sequencelogic;
|
|
|
|
#ifdef WIN32
|
|
#define STRDUP _strdup
|
|
#else
|
|
#define STRDUP strdup
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
unsigned int _sLineNo; // To keep track of the line number we are reading in, when loading JSON
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
T_JSON T_JSON::nulljson("");
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// C/Dtor
|
|
T_JSON::T_JSON(const char* name, T_JSON* p) : name(NULL)
|
|
{
|
|
if (name)
|
|
setname(name);
|
|
else
|
|
setname("");
|
|
type = J_NULL;
|
|
parent = p;
|
|
}
|
|
|
|
T_JSON::~T_JSON()
|
|
{
|
|
for (unsigned int i = 0; i < data.size(); ++i)
|
|
delete data[i];
|
|
data.clear();
|
|
if (name != NULL)
|
|
::free(name);
|
|
if (type == J_STRING)
|
|
::free(string);
|
|
}
|
|
|
|
void T_JSON::copyfrom(const T_JSON& o)
|
|
{
|
|
if (type != J_ARRAY)
|
|
setname(o.getname());
|
|
settype(o.gettype());
|
|
|
|
switch(o.gettype())
|
|
{
|
|
case J_NUMBER:
|
|
number = o.number;
|
|
break;
|
|
case J_STRING:
|
|
string = static_cast<char *>(::malloc(::strlen(o.string)+1));
|
|
::memset(string, 0, ::strlen(o.string)+1);
|
|
::strcpy(string, o.string);
|
|
break;
|
|
case J_BOOLEAN:
|
|
boolean = o.boolean;
|
|
break;
|
|
case J_ARRAY:
|
|
case J_OBJECT:
|
|
{
|
|
for (int i = 0; i < o.getnumelements(); ++i)
|
|
{
|
|
T_JSON *pTmpObj = new T_JSON();
|
|
T_JSON *pRhs = o.get(i);
|
|
pTmpObj->copyfrom(*pRhs);
|
|
add(pTmpObj);
|
|
}
|
|
}
|
|
break;
|
|
case J_NULL:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool T_JSON::merge(const T_JSON& o)
|
|
{
|
|
if (type != J_OBJECT || o.type != J_OBJECT)
|
|
return false;
|
|
for (int i = 0; i < o.getnumitems(); i++)
|
|
{
|
|
if (o[i].gettype() == J_OBJECT)
|
|
{
|
|
T_JSON* t = get(o[i].getname());
|
|
if (!t)
|
|
add(new T_JSON(o[i]));
|
|
else
|
|
t->merge(o[i]);
|
|
}
|
|
else
|
|
update(o[i]); // what if it's an array?
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool T_JSON::operator == (const T_JSON& rhs) const
|
|
{
|
|
if (this == &rhs)
|
|
return true;
|
|
|
|
bool bRetVal = false;
|
|
if (gettype() == rhs.gettype())
|
|
{
|
|
// Check names...
|
|
if ((getname() != NULL) &&
|
|
(rhs.getname() != NULL) &&
|
|
(::strcmp(getname(), rhs.getname()) == 0))
|
|
{
|
|
// Check data...
|
|
switch (gettype())
|
|
{
|
|
case J_NUMBER:
|
|
bRetVal = (number == rhs.number);
|
|
break;
|
|
case J_STRING:
|
|
bRetVal = (::strcmp(string, rhs.string) == 0);
|
|
break;
|
|
case J_BOOLEAN:
|
|
bRetVal = (boolean == rhs.boolean);
|
|
break;
|
|
case J_ARRAY:
|
|
if (data.size() == rhs.data.size())
|
|
{
|
|
bRetVal = true;
|
|
for (unsigned int i = 0; bRetVal && (i < data.size()); ++i)
|
|
bRetVal = (*data[i] == *rhs.data[i]);
|
|
}
|
|
break;
|
|
case J_OBJECT:
|
|
if (data.size() == rhs.data.size())
|
|
{
|
|
bRetVal = true;
|
|
for (unsigned int i = 0; bRetVal && (i < data.size()); ++i)
|
|
{
|
|
const T_JSON *pObj = const_cast<T_JSON &>(rhs).search(data[i]->getname());
|
|
if (pObj != NULL)
|
|
bRetVal = (*data[i] == *pObj);
|
|
else
|
|
bRetVal = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case J_NULL:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
void T_JSON::setname(const char *pName)
|
|
{
|
|
if (name == pName)
|
|
return; // If you pass getname() to setname(), it can seg fault.
|
|
|
|
if (name != NULL)
|
|
{
|
|
::free(name);
|
|
name = NULL;
|
|
}
|
|
if ((pName != NULL) && (pName[0] != '\0'))
|
|
{
|
|
size_t nLen = ::strlen(pName) + 1;
|
|
name = static_cast<char *>(::malloc(nLen));
|
|
::memset(name, 0, nLen);
|
|
::strcpy(name, pName);
|
|
}
|
|
}
|
|
|
|
T_JSON* T_JSON::search(const char *pNameToFind)
|
|
{
|
|
if ((pNameToFind == NULL) || (pNameToFind[0] == '\0'))
|
|
return NULL;
|
|
|
|
T_JSON *pRetVal = NULL;
|
|
for (unsigned int i = 0; (pRetVal == NULL) && (i < data.size()); ++i)
|
|
{
|
|
T_JSON *pTmp = data.at(i);
|
|
if ((pTmp->name != NULL) && (pTmp->name[0] != '\0') && (::strcmp(pTmp->name, pNameToFind) == 0))
|
|
pRetVal = pTmp;
|
|
}
|
|
return pRetVal;
|
|
}
|
|
|
|
void T_JSON::erase(const char *pKey)
|
|
{
|
|
if ((pKey != NULL) && (pKey[0] != '\0'))
|
|
{
|
|
T_JSON *pMember = NULL;
|
|
|
|
for (IntData::iterator iter = data.begin(); iter != data.end(); ++iter)
|
|
{
|
|
const char *pName = (*iter)->getname();
|
|
if ((pName != NULL) && (pName[0] != '\0') && (::strcmp((*iter)->getname(), pKey) == 0))
|
|
{
|
|
pMember = *iter;
|
|
data.erase(iter);
|
|
delete pMember;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Get ready to change the type
|
|
void T_JSON::free()
|
|
{
|
|
if (type == J_STRING)
|
|
::free(string);
|
|
else if (type == J_ARRAY || type == J_OBJECT)
|
|
{
|
|
for (unsigned int i = 0; i < data.size(); ++i)
|
|
delete data[i];
|
|
data.clear();
|
|
}
|
|
string = NULL;
|
|
type = J_NULL;
|
|
}
|
|
|
|
void T_JSON::settype(JSONTYPE t)
|
|
{
|
|
free();
|
|
type = t;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Error handling. For the moment I don't do much.
|
|
void T_JSON::error(const char* errorstr) const
|
|
{
|
|
fprintf(stderr, "JSON ERROR on object %s - %s\n", getname(), errorstr);
|
|
}
|
|
|
|
void T_JSON::error(const char* errorstr, FILE* f) const
|
|
{
|
|
fprintf(stderr, "JSON ERROR on object %s - %s\n", getname(), errorstr);
|
|
fprintf(stderr, " at about this point in the file: %s",
|
|
fgets((char*)alloca(J_MAXSTRLEN), J_MAXSTRLEN, f));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Set internals
|
|
void T_JSON::setnumber(double d)
|
|
{
|
|
settype(J_NUMBER);
|
|
number = d;
|
|
}
|
|
|
|
void T_JSON::setnumber(int64_t i)
|
|
{
|
|
settype(J_NUMBER);
|
|
number = static_cast<double>(i);
|
|
}
|
|
|
|
void T_JSON::setstring(const char* s)
|
|
{
|
|
settype(J_STRING);
|
|
string = ::STRDUP(s);
|
|
}
|
|
|
|
void T_JSON::setboolean(bool b)
|
|
{
|
|
settype(J_BOOLEAN);
|
|
boolean = b;
|
|
}
|
|
|
|
void T_JSON::add(T_JSON* j)
|
|
{
|
|
j->parent = this;
|
|
if (type == J_NULL)
|
|
settype(J_OBJECT);
|
|
checktype(J_OBJECT, J_ARRAY);
|
|
data.push_back(j);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// I/O
|
|
// Format a string replacing bad characters with \ escapes
|
|
static std::string format(const char* source)
|
|
{
|
|
const char* s = source;
|
|
size_t found = 0;
|
|
for (; *s; s++)
|
|
found += (*s < ' ' || *s > 0x7e || *s == '\\' || *s == '\"') ? 1 : 0;
|
|
if (!found)
|
|
return source;
|
|
|
|
// gotta format shit
|
|
s = source;
|
|
size_t nBufLen = ::strlen(source) + 10 + found * 5;
|
|
char* dest = static_cast<char *>(::alloca(nBufLen));
|
|
::memset(dest, 0, nBufLen);
|
|
for (char* d = dest; *s; s++)
|
|
{
|
|
switch(*s)
|
|
{
|
|
case '\\': *d++ = '\\'; *d++ = '\\'; break;
|
|
case '"': *d++ = '\\'; *d++ = '"'; break;
|
|
case '\a': *d++ = '\\'; *d++ = 'a'; break;
|
|
case '\b': *d++ = '\\'; *d++ = 'b'; break;
|
|
case '\f': *d++ = '\\'; *d++ = 'f'; break;
|
|
case '\n': *d++ = '\\'; *d++ = 'n'; break;
|
|
case '\r': *d++ = '\\'; *d++ = 'r'; break;
|
|
case '\t': *d++ = '\\'; *d++ = 't'; break;
|
|
case '\v': *d++ = '\\'; *d++ = 'v'; break;
|
|
default:
|
|
if (*s && (*s < 0x20 || *s > 0x7e))
|
|
{
|
|
// Write out actual bytes, as if it's UTF8
|
|
unsigned short nNumBytes = 0;
|
|
if ((*s & 0xfe) == 0xfe)
|
|
nNumBytes = 7;
|
|
else if ((*s & 0xfc) == 0xfc)
|
|
nNumBytes = 6;
|
|
else if ((*s & 0xf8) == 0xf8)
|
|
nNumBytes = 5;
|
|
else if ((*s & 0xf0) == 0xf0)
|
|
nNumBytes = 4;
|
|
else if ((*s & 0xe0) == 0xe0)
|
|
nNumBytes = 3;
|
|
else if ((*s & 0xc0) == 0xc0)
|
|
nNumBytes = 2;
|
|
else
|
|
{
|
|
// JSON doesn't support the '\xZZ' escape sequence. Use UNICODE instead. http://json.org/
|
|
sprintf(d, "\\u%04x", (unsigned)(unsigned char)(*s));
|
|
int nLen = (int)::strlen(d);
|
|
d += nLen;
|
|
}
|
|
|
|
if (nNumBytes > 0)
|
|
{
|
|
::memcpy(d, s, nNumBytes);
|
|
d += nNumBytes;
|
|
s += nNumBytes-1;
|
|
}
|
|
}
|
|
else
|
|
*d++ = *s;
|
|
break;
|
|
}
|
|
|
|
#ifndef NODEBUG
|
|
if (d >= dest + nBufLen - 5)
|
|
{
|
|
fprintf(stderr, "FATAL ERROR: buffer overrun in JSON string formatting"
|
|
"\nwhile attempting to encode string '%s'\n", source);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
std::string retVal(dest);
|
|
return retVal;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Dump the JSON to a file.
|
|
bool T_JSON::print(std::ostream &out, int indent /*= 4*/, bool bPrettyPrint /*= false*/) const
|
|
{
|
|
bool bRetVal = true;
|
|
|
|
int nActualIndent = ((bPrettyPrint) ? indent : 0);
|
|
if (parent && parent->type != J_ARRAY && getname() != NULL && getname()[0] != '\0')
|
|
out << std::string(nActualIndent, ' ') << "\"" << format(getname()) << "\":";
|
|
else
|
|
out << std::string(nActualIndent, ' ');
|
|
|
|
switch (type)
|
|
{
|
|
case J_NUMBER:
|
|
{
|
|
if (static_cast<long long>(number) == number)
|
|
out << /*std::setw(12) << */ static_cast<long long>(number);
|
|
else
|
|
out << /*std::setw(12) <<*/ number;
|
|
}
|
|
break;
|
|
case J_STRING:
|
|
out << "\"" << format(string) << "\"";
|
|
break;
|
|
case J_BOOLEAN:
|
|
out << ((boolean) ? "true" : "false");
|
|
break;
|
|
case J_ARRAY:
|
|
case J_OBJECT:
|
|
out << ((type == J_ARRAY) ? "[" : "{");
|
|
if (bPrettyPrint)
|
|
out << "\n";
|
|
for (int i = 0; i < getnumelements(); i++)
|
|
{
|
|
bRetVal = get(i)->print(out, nActualIndent+3, bPrettyPrint);
|
|
if (i < getnumelements()-1)
|
|
out << ",";
|
|
if (bPrettyPrint)
|
|
out << "\n";
|
|
}
|
|
out << std::string(nActualIndent, ' ') << ((type == J_ARRAY) ? "]" : "}");
|
|
break;
|
|
case J_NULL:
|
|
out << "null";
|
|
break;
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Generate a string version of the JSON object
|
|
std::string T_JSON::toString(int indent, bool pretty) const
|
|
{
|
|
std::stringstream jstr;
|
|
print(jstr, indent, pretty);
|
|
return jstr.str();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// get up to an ending ", processing stupid c-style \ escapes
|
|
static char* loadstring(JSONStream &stream)
|
|
{
|
|
char* buf = (char*) alloca(T_JSON::J_MAXSTRLEN);
|
|
char* s = buf;
|
|
char c = '"';
|
|
while ((c = stream.getChar()) != EOF)
|
|
{
|
|
char c1 = '\n', c2 = '\n';
|
|
if (c == '\\')
|
|
{
|
|
c = stream.getChar();
|
|
switch(c)
|
|
{
|
|
case '\\': c = '\\'; break;
|
|
case 'a': c = '\a'; break;
|
|
case 'b': c = '\b'; break;
|
|
case 'f': c = '\f'; break;
|
|
case 'n': c = '\n'; break;
|
|
case 'r': c = '\r'; break;
|
|
case 't': c = '\t'; break;
|
|
case 'v': c = '\v'; break;
|
|
case '/': c = '/'; break;
|
|
case '"': c = '\"'; break;
|
|
|
|
case 'x':
|
|
c1 = stream.getChar();
|
|
if (c1 > 'a') c1 -= 'a' - 10;
|
|
else if (c1 > 'A') c1 -= 'A' - 10;
|
|
else c1 -= '0';
|
|
c2 = stream.getChar();
|
|
if ((std::isdigit(c2, std::locale()) ||
|
|
(('A' <= c2) && (c2 <= 'F')) ||
|
|
(('a' <= c2) && (c2 <= 'f'))))
|
|
{
|
|
if (c2 > 'a') c2 -= 'a' - 10;
|
|
else if (c2 > 'A') c2 -= 'A' - 10;
|
|
else c2 -= '0';
|
|
c = c1 << 4 | c2;
|
|
}
|
|
else
|
|
{
|
|
stream.putChar(c2);
|
|
c = c1;
|
|
}
|
|
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
c1 = stream.getChar();
|
|
c1 -= '0';
|
|
c2 = stream.getChar();
|
|
c2 -= '0';
|
|
c = ((c - '0') << 6) | (c1 << 3) | c2;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
std::cerr << "Invalid escape character '" << c << "' in JSON file at line " << _sLineNo << std::endl;
|
|
c = stream.getChar();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (c == '"')
|
|
break;
|
|
|
|
*s++ = c;
|
|
}
|
|
|
|
if (c == EOF)
|
|
return NULL;
|
|
|
|
*s = '\0';
|
|
return ::STRDUP(buf);
|
|
}
|
|
|
|
// find the next non-space in the file.
|
|
static int skipspaces(JSONStream &stream, unsigned int &nLineNo)
|
|
{
|
|
for (;;)
|
|
{
|
|
char c = EOF;
|
|
c = stream.getChar();
|
|
if (c == '\n')
|
|
++nLineNo;
|
|
if (c == '/') // allow '//' followed by comment.
|
|
{
|
|
char newc = EOF;
|
|
newc = stream.getChar();
|
|
if (newc == '/')
|
|
{
|
|
while (newc != EOF && newc != '\n')
|
|
newc = stream.getChar();
|
|
if (newc == '\n')
|
|
++nLineNo;
|
|
continue;
|
|
}
|
|
if (newc != EOF)
|
|
stream.putChar(newc);
|
|
}
|
|
|
|
if (c == EOF || !isspace(static_cast<unsigned char>(c)))
|
|
return c;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
bool T_JSON::load(JSONStream &stream)
|
|
{
|
|
char c = skipspaces(stream, _sLineNo);
|
|
|
|
// get the name, if there is one
|
|
if (c == '"')
|
|
{
|
|
char* s = loadstring(stream);
|
|
if (!s)
|
|
{
|
|
std::stringstream msg;
|
|
msg << "Unterminated name string in JSON file at line " << _sLineNo;
|
|
error(msg.str().c_str());
|
|
return false;
|
|
}
|
|
c = skipspaces(stream, _sLineNo);
|
|
if (c != ':')
|
|
{
|
|
stream.putChar(c);
|
|
settype(J_STRING);
|
|
string = s;
|
|
return true;
|
|
}
|
|
setname(s);
|
|
::free(s);
|
|
c = skipspaces(stream, _sLineNo);
|
|
}
|
|
|
|
switch(c)
|
|
{
|
|
case '{':
|
|
case '[':
|
|
{
|
|
int expect = (c == '{' ? '}' : ']');
|
|
settype(c == '{' ? J_OBJECT : J_ARRAY);
|
|
c = skipspaces(stream, _sLineNo);
|
|
if (c == expect)
|
|
return true;
|
|
stream.putChar(c);
|
|
|
|
for(;;)
|
|
{
|
|
T_JSON* j = new T_JSON("", this);
|
|
if (!j->load(stream))
|
|
{
|
|
delete j;
|
|
return false;
|
|
}
|
|
add(j);
|
|
c = skipspaces(stream, _sLineNo);
|
|
if (c == expect)
|
|
break;
|
|
else if (c != ',')
|
|
{
|
|
std::stringstream msg;
|
|
msg << "Expected comma (',') or right " << ((expect == '}') ? "brace" : "bracket") << " at line " << _sLineNo;
|
|
error(msg.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 't': case 'T':
|
|
{
|
|
char c1 = 'z', c2 = 'z', c3 = 'z';
|
|
c1 = stream.getChar();
|
|
c2 = stream.getChar();
|
|
c3 = stream.getChar();
|
|
if ((c1 != 'r' && c1 != 'R') ||
|
|
(c2 != 'u' && c2 != 'U') ||
|
|
(c3 != 'e' && c3 != 'E'))
|
|
goto broken;
|
|
}
|
|
settype(J_BOOLEAN);
|
|
boolean = true;
|
|
break;
|
|
|
|
case 'f': case 'F':
|
|
{
|
|
char c1 = 'z', c2 = 'z', c3 = 'z', c4 = 'z';
|
|
c1 = stream.getChar();
|
|
c2 = stream.getChar();
|
|
c3 = stream.getChar();
|
|
c4 = stream.getChar();
|
|
if ((c1 != 'a' && c1 != 'A') ||
|
|
(c2 != 'l' && c2 != 'L') ||
|
|
(c3 != 's' && c3 != 'S') ||
|
|
(c4 != 'e' && c4 != 'E'))
|
|
goto broken;
|
|
}
|
|
settype(J_BOOLEAN);
|
|
boolean = false;
|
|
break;
|
|
break;
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case '+': case '-':
|
|
{
|
|
stream.putChar(c);
|
|
settype(J_NUMBER);
|
|
number = stream.getNum();
|
|
}
|
|
break;
|
|
|
|
case '"':
|
|
settype(J_STRING);
|
|
if (!(string = loadstring(stream)))
|
|
{
|
|
std::stringstream msg;
|
|
msg << "Unterminated string at line " << _sLineNo;
|
|
error(msg.str().c_str());
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 'n': case 'N':
|
|
{
|
|
char c1 = 'z', c2 = 'z', c3 = 'z';
|
|
c1 = stream.getChar();
|
|
c2 = stream.getChar();
|
|
c3 = stream.getChar();
|
|
if ((c1 != 'u' && c1 != 'U') ||
|
|
(c2 != 'l' && c2 != 'L') ||
|
|
(c3 != 'l' && c3 != 'L'))
|
|
goto broken;
|
|
}
|
|
break;
|
|
|
|
case ',':
|
|
// This is really an error. You shouldn't have a key with no value at all.
|
|
stream.putChar(c);
|
|
break;
|
|
|
|
case EOF:
|
|
//
|
|
error("JSON file is truncated.");
|
|
return false;
|
|
|
|
default:
|
|
broken:
|
|
error("JSON file format not valid");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
bool T_JSON::store(const char* filename, bool bPrettyPrint /*= true*/) const
|
|
{
|
|
bool bRetVal = false;
|
|
|
|
std::ofstream outFile (filename, std::ios_base::out|std::ios_base::trunc);
|
|
if (!outFile.bad())
|
|
{
|
|
bRetVal = print(outFile, 0, bPrettyPrint);
|
|
outFile.close();
|
|
}
|
|
return bRetVal;
|
|
}
|
|
|
|
bool T_JSON::load(const char* filename)
|
|
{
|
|
_sLineNo = 1;
|
|
FILE *pFile = ::fopen(filename, "r");
|
|
if (!pFile)
|
|
return false;
|
|
bool bRetVal = load(pFile);
|
|
::fclose(pFile);
|
|
return bRetVal;
|
|
}
|
|
|
|
bool T_JSON::load(FILE* f)
|
|
{
|
|
JSONStream stream(f);
|
|
return load(stream);
|
|
}
|
|
|
|
bool T_JSON::loadFromString(const char *pJSONStr)
|
|
{
|
|
_sLineNo = 1;
|
|
bool bRetVal = false;
|
|
if ((pJSONStr != NULL) && (pJSONStr[0] != '\0'))
|
|
{
|
|
JSONStream stream(pJSONStr);
|
|
bRetVal = load(stream);
|
|
}
|
|
return bRetVal;
|
|
}
|