From 2d2f6b254bef2c52e59e610f84cf60c6a0d832b1 Mon Sep 17 00:00:00 2001 From: peri4 Date: Mon, 26 Sep 2022 17:49:58 +0300 Subject: [PATCH] add PIJSON, yet without doc --- libs/main/core/pijson.cpp | 340 ++++++++++++++++++++++++++++++++++++++ libs/main/core/pijson.h | 122 ++++++++++++++ main.cpp | 217 ++++++------------------ 3 files changed, 512 insertions(+), 167 deletions(-) create mode 100644 libs/main/core/pijson.cpp create mode 100644 libs/main/core/pijson.h diff --git a/libs/main/core/pijson.cpp b/libs/main/core/pijson.cpp new file mode 100644 index 00000000..7b93a102 --- /dev/null +++ b/libs/main/core/pijson.cpp @@ -0,0 +1,340 @@ +/* + 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 . +*/ +#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"; +} diff --git a/libs/main/core/pijson.h b/libs/main/core/pijson.h new file mode 100644 index 00000000..4b2f6344 --- /dev/null +++ b/libs/main/core/pijson.h @@ -0,0 +1,122 @@ +/*! \file pijson.h + * \ingroup Core + * \brief + * \~english JSON class + * \~russian Класс JSON +*/ +/* + 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 . +*/ + +#ifndef PIJSON_H +#define PIJSON_H + +#include "pivariant.h" + + +//! \ingroup Core +//! \~\brief +//! \~english JSON class. +//! \~russian Класс JSON. +class PIP_EXPORT PIJSON { + friend PICout operator <<(PICout s, const PIJSON & v); +public: + enum Type { + Invalid, + Null, + Boolean, + Number, + String, + Object, + Array + }; + enum PrintType { + Compact, + Tree + }; + PIJSON() {} + PIJSON(const PIJSON & o) = default; + + static PIJSON newObject(); + static PIJSON newArray(); + + const PIString & name() const {return c_name;} + const PIVector & array() const {return c_array;} + const PIMap & object() const {return c_object;} + const PIVariant & value() const {return c_value;} + bool toBool() const {return c_value.toBool();} + int toInt() const {return c_value.toInt();} + double toDouble() const {return c_value.toDouble();} + PIString toString() const {return c_value.toString();} + Type type() const {return c_type;} + bool isValid() const {return c_type != Invalid;} + bool isObject() const {return c_type == Object;} + bool isArray() const {return c_type == Array;} + void setValue(const PIVariant & v); + void clear(); + int size() const; + bool contains(const PIString & key) const; + void resize(int new_size); + + const PIJSON & operator[](int index) const; + PIJSON & operator[](int index); + PIJSON & operator=(const PIVariant & v) {setValue(v); return *this;} + PIJSON & operator=(const PIJSON & v); + + PIJSON operator[](const PIString & key) const; + PIJSON & operator[](const PIString & key); + PIJSON operator[](const char * key) const {return (*this)[PIString::fromUTF8(key)];} + PIJSON & operator[](const char * key) {return (*this)[PIString::fromUTF8(key)];} + + PIString toJSON() const; + static PIJSON fromJSON(PIString str); + +private: + static PIJSON & nullEntry(); + static PIString parseName(PIString & s); + static PIJSON parseValue(PIString & s); + static PIJSON parseObject(PIString s); + static PIJSON parseArray(PIString s); + static PIString stringMask(const PIString & s);; + static PIString stringUnmask(const PIString & s);; + static void print(PIString & s, const PIJSON & v, PIString tab, bool transform = false, bool comma = false); + + PIString c_name; + PIVariant c_value; + PIVector c_array; + PIMap c_object; + Type c_type = Invalid; +}; + + + +//! \relatesalso PICout +//! \~english Output operator to \a PICout. +//! \~russian Оператор вывода в \a PICout. +inline PICout operator <<(PICout s, const PIJSON & v) { + s.space(); + s.saveAndSetControls(0); + PIString str; + PIJSON::print(str, v, ""); + s << str; + s.restoreControls(); + return s; +} + + +#endif // PICONSTCHARS_H diff --git a/main.cpp b/main.cpp index 22dad5b9..b2db81f4 100644 --- a/main.cpp +++ b/main.cpp @@ -2,178 +2,61 @@ #include "piiostream.h" #include "pibytearray.h" #include "pimathbase.h" +#include "pijson.h" using namespace PICoutManipulators; -template -T toDecimal(const PIString & s) { - int part = 0, exp = 0; - bool negative = false, negative_exp = false, err = false, has_digit = false; - T ret = 0., frac = 0., frac_delim = 1.; - for (const PIChar pc: s) { - char c = pc.toAscii(); - switch (part) { - case 0: // sign - if (pc.isSpace()) continue; - if (c >= '0' && c <= '9') { - has_digit = true; - ret = c - '0'; - part = 1; - continue; - } - if (c == '+') { - part = 1; - continue; - } - if (c == '-') { - negative = true; - part = 1; - continue; - } - if (c == '.' || c == ',') { - part = 2; - continue; - } - err = true; - break; - case 1: // integer - if (c >= '0' && c <= '9') { - has_digit = true; - ret = ret * 10 + (c - '0'); - continue; - } - if (c == '.' || c == ',') { - part = 2; - continue; - } - if ((c == 'e' || c == 'E') && has_digit) { - part = 3; - continue; - } - err = true; - break; - case 2: // fractional - if (c >= '0' && c <= '9') { - has_digit = true; - frac = frac * 10 + (c - '0'); - frac_delim *= 10.; - //piCout << frac << frac_delim; - //printf("%.20f %.20f\n", frac, T(c - '0') / frac_delim); - continue; - } - if ((c == 'e' || c == 'E') && has_digit) { - part = 3; - continue; - } - err = true; - break; - case 3: // exponent with sign - if (c == '+') { - part = 4; - continue; - } - if (c == '-') { - negative_exp = true; - part = 4; - continue; - } - case 4: // exponent - if (c >= '0' && c <= '9') { - exp = (exp * 10) + (c - '0'); - continue; - } - err = true; - break; - } - if (err) break; - } - frac /= frac_delim; - ret += frac; - if (negative && has_digit) ret = -ret; - if (exp > 0) { - if (negative_exp) ret /= pow10((T)exp); - else ret *= pow10((T)exp); - } - return ret; -} +typedef PIMap PIVariantMap; +typedef PIVector PIVariantVector; + +REGISTER_VARIANT(PIVariantMap); +REGISTER_VARIANT(PIVariantVector); + +const char J[] = + "[ \n" + "{ \n" + " \"idFligth\":\"456123\", \n" + " \"FligthPath\": \"d:/orders/nicirt/BSK-52(BBR)/219031.001/EBN-NM-BBR.IMG\",\n" + " \"FligthDataPath\": \"\", \n" + " \"Passport\": \n" + " { \n" + " \"id\": \"\", \n" + " \"TypePlane\": \"\", \n" + " \"FA_PLANEBORT\": \"Ka52-10\" \n" + " } \n" + " }, [1.1,2,3,4,{\"a\":null},{\"bool\":true,\"bool2\":false}] \n" + "] \n" +; -inline void test(const PIString & s) { - double av = atof(s.dataAscii()); - double v = toDecimal(s); - printf("\n"); - piCout << s << "=" << v << av; - printf("atof = %.20f\n", av); - printf(" PIP = %.20f\n", v); -} int main(int argc, char * argv[]) { - /*test(" 123 "); - test("\n123 "); - test("\t123 "); - test(" +123 "); - test(" ++123 "); - test(" + 123 "); - test(" -123 "); - test(" --123 "); - test(" - 123 "); - test("123.1"); - test("123 .1"); - test("123 . 1"); - test("123.+1"); - test("123.-1"); - test("123E2"); - test("123E+2"); - test("123E +2"); - test("123E+ 2"); - test("123E + 2"); - test("123 E+2"); - test("123 E +2"); - test("123 E+ 2"); - test("123 E + 2"); - test("123E+2.5"); - test("123.4E+2"); - test("123.4.5E+2"); - test("123EE+2"); - test("123e+2"); - test("123e++2"); - test("E+2"); - test("1E+2"); - test("1.E+2"); - test(".E+2"); - test(".1E-2"); - test("+E2"); - test("nan"); - test("inf");*/ - test("-"); - test("-0"); - test("-."); - test("-.0"); - test(".23456123456789987654"); - test("-E+100"); - - return 0; - - PITimeMeasurer tm; - PIString s("1.23456789"); - int cnt = 10000000; - int el_o = 0, el_n = 0; - double v = 0.; - NO_UNUSED(v); - - tm.reset(); - piForTimes (cnt) { - v = s.toDouble(); - } - el_o = tm.elapsed_u(); - piCout << v << el_o; - - tm.reset(); - piForTimes (cnt) { - v = toDecimal(s); - } - el_n = tm.elapsed_u(); - piCout << v << el_n; - - piCout << (double)el_o / el_n; + PIString s; + s = PIString::fromUTF8(J).trim(); + s = PIString::fromUTF8("{\"st\\u0426r\":\"\\\\ \\\" \\u0425\\u0430\\n\"}"); + //s = PIString::fromUTF8("{\"str\":\"Ха\"}"); + PIJSON json = PIJSON::fromJSON(s); + piCout << json; + piCout << json.toJSON(); + //json.resize(3); + //json["0"].setValue(123); + //json["1"].setValue("sec"); + //json["2"]["f"].setValue(true); + //json["2"]["s"].setValue(-1);*/ + //json[0]["Passport"]["id"] = 0xFF; + //piCout << json; + json = PIJSON::newObject(); + json["num"] = 123; + json["str__"] = PIString::fromUTF8("string русский ℃ 😆 "); + json["obj"]["b"] = true; + json["obj"]["i"] = -1; + auto & arr(json["obj"]["arr"]); + arr.resize(3); + arr[0] = 10; + arr[1] = 11.2E-1; + arr[2] = "!!!"; + json["obj2"] = json["obj"]; + //piCout << json; + piCout << json.toJSON(); + piCout << json["str__"].toString().toUTF8(); return 0; }