Files
pip/libs/main/core/pijson.cpp
2022-09-26 17:49:58 +03:00

341 lines
7.5 KiB
C++

/*
PIP - Platform Independent Primitives
JSON class
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pijson.h"
//! \~\class PIJSON pijson.h
//! \~\details
//! \~english \section PIJSON_sec0 Synopsis
//! \~russian \section PIJSON_sec0 Краткий обзор
//! \~english
//!
//! \~russian
//!
PIJSON PIJSON::newObject() {
PIJSON ret;
ret.c_type = Object;
return ret;
}
PIJSON PIJSON::newArray() {
PIJSON ret;
ret.c_type = Array;
return ret;
}
void PIJSON::setValue(const PIVariant & v) {
c_value = v;
switch (v.type()) {
case PIVariant::pivBool: c_type = Boolean; break;
case PIVariant::pivUChar:
case PIVariant::pivShort:
case PIVariant::pivUShort:
case PIVariant::pivInt:
case PIVariant::pivUInt:
case PIVariant::pivLLong:
case PIVariant::pivULLong:
case PIVariant::pivFloat:
case PIVariant::pivDouble:
case PIVariant::pivLDouble: c_type = Number; break;
case PIVariant::pivString: c_type = String; break;
default: c_type = Invalid; break;
}
}
void PIJSON::clear() {
c_type = Invalid;
c_value = PIVariant();
c_name.clear();
c_object.clear();
c_array.clear();
}
int PIJSON::size() const {
if (!isArray())
return 0;
return c_array.size_s();
}
bool PIJSON::contains(const PIString & key) const {
if (!isObject()) return false;
return c_object.contains(key);
}
void PIJSON::resize(int new_size) {
c_array.resize(new_size);
c_type = Array;
}
const PIJSON & PIJSON::operator[](int index) const {
if (!isArray())
return nullEntry();
return c_array[index];
}
PIJSON & PIJSON::operator[](int index) {
c_type = Array;
PIJSON & ret(c_array[index]);
return ret;
}
PIJSON & PIJSON::operator=(const PIJSON & v) {
c_type = v.c_type;
c_value = v.c_value;
c_object = v.c_object;
c_array = v.c_array;
return *this;
}
PIJSON PIJSON::operator[](const PIString & key) const {
if (!isObject())
return nullEntry();
if (!c_object.contains(key))
return nullEntry();
return c_object.value(key);
}
PIJSON & PIJSON::operator[](const PIString & key) {
c_type = Object;
PIJSON & ret(c_object[key]);
ret.c_name = key;
return ret;
}
PIString PIJSON::toJSON() const {
PIString ret;
print(ret, *this, "", true);
return ret;
}
PIJSON PIJSON::fromJSON(PIString str) {
return parseValue(str);
}
PIJSON & PIJSON::nullEntry() {
static PIJSON ret;
ret.clear();
return ret;
}
PIString PIJSON::parseName(PIString & s) {
//piCout << "\n\n\n parseName" << s;
PIString ret;
ret = s.takeRange('"', '"');
s.trim();
if (s.isEmpty()) return PIString();
if (s[0] != ':') return PIString();
s.remove(0).trim();
return PIJSON::stringUnmask(ret);
}
PIJSON PIJSON::parseValue(PIString & s) {
PIJSON ret;
//piCout << "\n\n\n parseValue" << s;
s.trim();
if (s.isEmpty()) return ret;
if (s[0] == '{')
ret = parseObject(s.takeRange('{', '}'));
else if (s[0] == '[')
ret = parseArray(s.takeRange('[', ']'));
else {
s.trim();
if (s.startsWith('"')) {
ret.c_type = PIJSON::String;
ret.c_value = PIJSON::stringUnmask(s.takeRange('"', '"'));
} else {
PIString value;
int ind = s.find(',');
if (ind >= 0) {
value = s.takeLeft(ind).trim().toLowerCase();
} else {
value = s;
s.clear();
}
//piCout << "\n\n\n parseValue value = \"" << value << "\"";
if (value == "null") {
ret.c_type = PIJSON::Null;
} else if (value == "true") {
ret.c_type = PIJSON::Boolean;
ret.c_value = true;
} else if (value == "false") {
ret.c_type = PIJSON::Boolean;
ret.c_value = false;
} else {
ret.c_type = PIJSON::Number;
ret.c_value = value;
}
}
}
return ret;
}
PIJSON PIJSON::parseObject(PIString s) {
//piCout << "\n\n\n parseObject" << s;
PIJSON ret;
ret.c_type = PIJSON::Object;
while (s.isNotEmpty()) {
PIString name = PIJSON::stringUnmask(parseName(s));
PIJSON value = parseValue(s);
auto & child(ret.c_object[name]);
child = value;
child.c_name = name;
s.trim();
if (s.isEmpty()) break;
if (s[0] != ',') break;
s.remove(0);
}
return ret;
}
PIJSON PIJSON::parseArray(PIString s) {
//piCout << "\n\n\n parseArray" << s;
PIJSON ret;
ret.c_type = PIJSON::Array;
while (s.isNotEmpty()) {
PIJSON value = parseValue(s);
ret.c_array << value;
s.trim();
if (s.isEmpty()) break;
if (s[0] != ',') break;
s.remove(0);
}
return ret;
}
PIString PIJSON::stringMask(const PIString & s) {
PIString ret;
for (auto c: s) {
if (!c.isAscii()) {
ret += "\\u" + PIString::fromNumber(c.unicode16Code(), 16).expandLeftTo(4, '0');
} else {
char ca = c.toAscii();
switch (ca) {
case '"' : ret += "\\\""; break;
case '\\': ret += "\\\\"; break;
case '/' : ret += "\\/" ; break;
case '\b': ret += "\\b" ; break;
case '\f': ret += "\\f" ; break;
case '\n': ret += "\\n" ; break;
case '\r': ret += "\\r" ; break;
case '\t': ret += "\\t" ; break;
default: ret += ca;
}
}
}
return ret;
}
PIString PIJSON::stringUnmask(const PIString & s) {
PIString ret;
for (int i = 0; i < s.size_s(); ++i) {
char ca = s[i].toAscii();
if (ca == '\\') {
if (i == s.size_s() - 1) continue;
++i;
char na = s[i].toAscii();
switch (na) {
case '"' : ret += '\"'; break;
case '\\': ret += '\\'; break;
case '/' : ret += '/' ; break;
case 'b' : ret += '\b'; break;
case 'f' : ret += '\f'; break;
case 'n' : ret += '\n'; break;
case 'r' : ret += '\r'; break;
case 't' : ret += '\t'; break;
case 'u' :
ret += PIChar(s.mid(i + 1, 4).toUShort(16));
i += 4;
break;
default: break;
}
} else {
ret += s[i];
}
}
return ret;
}
void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool transform, bool comma) {
s += tab;
/*
switch (v.c_type) {
case JSONEntry::Invalid: s << "(Invalid)"; break;
case JSONEntry::Null: s << "(Null)"; break;
case JSONEntry::Boolean: s << "(Boolean)"; break;
case JSONEntry::Number: s << "(Number)"; break;
case JSONEntry::String: s << "(String)"; break;
case JSONEntry::Object: s << "(Object)"; break;
case JSONEntry::Array: s << "(Array)"; break;
}
*/
if (v.name().isNotEmpty()) s += '"' + (transform ? stringMask(v.name()) : v.name()) + "\": ";
switch (v.c_type) {
case PIJSON::Invalid: break;
case PIJSON::Null: s += "null"; break;
case PIJSON::Boolean: s += PIString::fromBool(v.c_value.toBool()); break;
case PIJSON::Number: s += v.c_value.toString(); break;
case PIJSON::String: s += '"' + (transform ? stringMask(v.c_value.toString()) : v.c_value.toString()) + '"'; break;
case PIJSON::Object:
s += "{\n";
{
PIString ntab = tab + " ";
auto it = v.c_object.makeIterator();
int cnt = 0;
while (it.next())
print(s, it.value(), ntab, transform, ++cnt < v.c_object.size_s());
}
s += tab + "}";
break;
case PIJSON::Array:
s += "[\n";
{
PIString ntab = tab + " ";
for (int i = 0; i < v.c_array.size_s(); ++i)
print(s, v.c_array[i], ntab, transform, i < v.c_array.size_s() - 1);
}
s += tab + "]";
break;
}
if (comma) s += ',';
s += "\n";
}