diff --git a/libs/main/core/pijson.cpp b/libs/main/core/pijson.cpp index 7b93a102..203c5318 100644 --- a/libs/main/core/pijson.cpp +++ b/libs/main/core/pijson.cpp @@ -26,7 +26,67 @@ //! \~english //! //! \~russian +//! JSON - это древовидная структура, каждый элемент которой может бить либо +//! парой имя:значение, либо массивом, либо объектом, т.е. именованным +//! списком элементов. Корневой элемент JSON может быть либо массивом, +//! либо объектом. //! +//! Массивы заключены в квадратные скобки [], их элементы не имеют имени +//! и разделяются запятыми. +//! +//! Объекты заключены в фигурные скобки {}, их элементы имеют имя +//! и разделяются запятыми. +//! +//! +//! \~english \section PIJSON_sec1 PIJSON tree +//! \~russian \section PIJSON_sec1 Дерево PIJSON +//! \~english +//! +//! \~russian +//! %PIJSON представляет собой элемент дерева JSON. Каждый элемент имеет тип (\a type()) +//! и может иметь имя (\a name()). Если это конечный элемент,то он будет иметь значение, +//! доступное через \a value(), \a toBool(), \a toInt(), \a toDouble() или \a toString(). +//! +//! Если элемент преставляет собой массив, то его размер доступен через \a size(), +//! а элементы массива через целочисленный оператор []. Весь массив доступен через \a array(). +//! +//! Если элемент преставляет собой объект, то его размер доступен через \a size(), +//! а элементы объекта через строковый оператор []. Проверить наличие элемента по имени можно +//! с помощью \a contains(). Весь объект доступен через \a object(). +//! +//! Создать дерево из текстового представления JSON можно с помощью \a fromJSON(), а +//! преобразовать в текст с помощью \a toJSON(). +//! +//! +//! \~english \section PIJSON_sec2 PIJSON creation +//! \~russian \section PIJSON_sec2 Создание PIJSON +//! \~english +//! +//! \~russian +//! Для создания нового дерева необходимо лишь создать пустой корневой PIJSON и заполнить его +//! значениями, массивами или объектами с помощью целочисленного или строкового оператора []. +//! В зависимости от типа аргумента элемент преобразуется либо в массив, либо в объект. +//! +//! При приравнивании PIJSON к какому-либо значению, его тип автоматически установится в нужный. +//! При обращении на запись целочисленным оператором [] размер массива автоматически увеличится +//! при необходимости. +//! +//! +//! \~english \section PIJSON_sec3 Mask/unmask +//! \~russian \section PIJSON_sec3 Маскирование/размаскирование +//! \~english +//! +//! \~russian +//! Строковые значения в стандарте JSON могут иметь в явном виде только печатные символы, +//! спецсимволы и юникод должны быть преобразованы маскированием, т.е., например, вместо +//! символа новой строки должно быть "\n", а не-ASCII символы должны быть в виде "\uXXXX". +//! +//! Оператор вывода в PICout не выполняет маскирования строк. +//! +//! Методы \a toJSON() и \a fromJSON() маскируют и размаскируют строковые поля. +//! +//! + PIJSON PIJSON::newObject() { @@ -43,6 +103,38 @@ PIJSON PIJSON::newArray() { } +PIJSON PIJSON::newString(const PIString & v) { + PIJSON ret; + ret = v; + return ret; +} + + +const PIVector & PIJSON::array() const { + if (!isArray()) + return nullEntry().c_array; + return c_array; +} + + +const PIMap & PIJSON::object() const { + if (!isObject()) + return nullEntry().c_object; + return c_object; +} + + +//! \details +//! \~english +//! If "v" type is boolean set type to \a PIJSON::Boolean.\n +//! If "v" type is any numeric set type to \a PIJSON::Number.\n +//! If "v" type is string set type to \a PIJSON::String.\n +//! In case of any other types set element type to \a PIJSON::Invalid. +//! \~russian +//! Если тип "v" логический, то устанавливает тип в \a PIJSON::Boolean.\n +//! Если тип "v" любой численный, то устанавливает тип в \a PIJSON::Number.\n +//! Если тип "v" строковый, то устанавливает тип в \a PIJSON::String.\n +//! Если тип "v" любой другой, то устанавливает тип в \a PIJSON::Invalid. void PIJSON::setValue(const PIVariant & v) { c_value = v; switch (v.type()) { @@ -73,9 +165,11 @@ void PIJSON::clear() { int PIJSON::size() const { - if (!isArray()) - return 0; - return c_array.size_s(); + if (isArray()) + return c_array.size_s(); + if (isObject()) + return c_object.size_s(); + return 0; } @@ -86,8 +180,8 @@ bool PIJSON::contains(const PIString & key) const { void PIJSON::resize(int new_size) { - c_array.resize(new_size); c_type = Array; + c_array.resize(new_size, newString()); } @@ -100,6 +194,8 @@ const PIJSON & PIJSON::operator[](int index) const { PIJSON & PIJSON::operator[](int index) { c_type = Array; + if (index >= c_array.size_s()) + c_array.resize(index + 1, newString()); PIJSON & ret(c_array[index]); return ret; } @@ -131,9 +227,9 @@ PIJSON & PIJSON::operator[](const PIString & key) { } -PIString PIJSON::toJSON() const { +PIString PIJSON::toJSON(PrintType print_type) const { PIString ret; - print(ret, *this, "", true); + print(ret, *this, "", print_type == Tree, true); return ret; } @@ -294,8 +390,8 @@ PIString PIJSON::stringUnmask(const PIString & s) { } -void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool transform, bool comma) { - s += tab; +void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform, bool comma) { + if (spaces) s += tab; /* switch (v.c_type) { case JSONEntry::Invalid: s << "(Invalid)"; break; @@ -307,7 +403,10 @@ void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool transform, case JSONEntry::Array: s << "(Array)"; break; } */ - if (v.name().isNotEmpty()) s += '"' + (transform ? stringMask(v.name()) : v.name()) + "\": "; + if (v.name().isNotEmpty()) { + s += '"' + (transform ? stringMask(v.name()) : v.name()) + "\":"; + if (spaces) s += ' '; + } switch (v.c_type) { case PIJSON::Invalid: break; case PIJSON::Null: s += "null"; break; @@ -315,26 +414,30 @@ void PIJSON::print(PIString & s, const PIJSON & v, PIString tab, bool transform, 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"; + s += "{"; + if (spaces) 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()); + print(s, it.value(), ntab, spaces, transform, ++cnt < v.c_object.size_s()); } - s += tab + "}"; + if (spaces) s += tab; + s += "}"; break; case PIJSON::Array: - s += "[\n"; + s += "["; + if (spaces) 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); + print(s, v.c_array[i], ntab, spaces, transform, i < v.c_array.size_s() - 1); } - s += tab + "]"; + if (spaces) s += tab; + s += "]"; break; } if (comma) s += ','; - s += "\n"; + if (spaces) s += "\n"; } diff --git a/libs/main/core/pijson.h b/libs/main/core/pijson.h index 4b2f6344..a1ab327b 100644 --- a/libs/main/core/pijson.h +++ b/libs/main/core/pijson.h @@ -36,56 +36,145 @@ class PIP_EXPORT PIJSON { friend PICout operator <<(PICout s, const PIJSON & v); public: + + //! \~english + //! Type of JSON tree element + //! \~russian + //! Тип элемента дерева JSON enum Type { - Invalid, - Null, - Boolean, - Number, - String, - Object, - Array + Invalid /*! \~english Invalid type \~russian Недействительный тип */, + Null /*! \~english Without value, null \~russian Без значения, null */, + Boolean /*! \~english Boolean, /b true or /b false \~russian Логическое, /b true или /b false */, + Number /*! \~english Integer or floating-point number \~russian Целое либо число с плавающей точкой */, + String /*! \~english Text \~russian Текст */, + Object /*! \~english Object, {} \~russian Объект, {} */, + Array /*! \~english Array, [] \~russian Массив, [] */ }; + + //! \~english + //! Generate JSON variant + //! \~russian + //! Вариант генерации JSON enum PrintType { - Compact, - Tree + Compact /*! \~english Without spaces, minimum size \~russian Без пробелов, минимальный размер */, + Tree /*! \~english With spaces and new-lines, human-readable \~russian С пробелами и новыми строками, читаемый человеком */ }; + + //! \~english Contructs invalid %PIJSON. + //! \~russian Создает недействительный %PIJSON. PIJSON() {} PIJSON(const PIJSON & o) = default; - static PIJSON newObject(); - static PIJSON newArray(); - + //! \~english Returns name of element, or empty string if it doesn`t have name. + //! \~russian Возвращает имя элемента, либо пустую строку, если имени нет. const PIString & name() const {return c_name;} - const PIVector & array() const {return c_array;} - const PIMap & object() const {return c_object;} + + //! \~english Returns elements array of this element, or empty array if element is not \a PIJSON::Array. + //! \~russian Возвращает массив элементов этого элемента, либо пустой массив, если тип элемента не \a PIJSON::Array. + const PIVector & array() const; + + //! \~english Returns elements map of this element, or empty map if element is not \a PIJSON::Object. + //! \~russian Возвращает словарь элементов этого элемента, либо пустой словарь, если тип элемента не \a PIJSON::Object. + const PIMap & object() const; + + //! \~english Returns element value. + //! \~russian Возвращает значение элемента. const PIVariant & value() const {return c_value;} + + //! \~english Returns element value as bool. + //! \~russian Возвращает значение элемента как логическое. bool toBool() const {return c_value.toBool();} + + //! \~english Returns element value as integer number. + //! \~russian Возвращает значение элемента как целое число. int toInt() const {return c_value.toInt();} + + //! \~english Returns element value as floating-point number. + //! \~russian Возвращает значение элемента как число с плавающей точкой. double toDouble() const {return c_value.toDouble();} + + //! \~english Returns element value as string, valid for all types. + //! \~russian Возвращает значение элемента как строка, действительно для всех типов. PIString toString() const {return c_value.toString();} + + + //! \~english Returns element type. + //! \~russian Возвращает тип элемента. Type type() const {return c_type;} + + //! \~english Returns if element is valid. + //! \~russian Возвращает действителен ли элемент. bool isValid() const {return c_type != Invalid;} + + //! \~english Returns if element is \a PIJSON::Object. + //! \~russian Возвращает является ли элемент \a PIJSON::Object. bool isObject() const {return c_type == Object;} + + //! \~english Returns if element is \a PIJSON::Array. + //! \~russian Возвращает является ли элемент \a PIJSON::Array. bool isArray() const {return c_type == Array;} + + //! \~english Set value and type of element from "v". + //! \~russian Устанавливает значение и тип элемента из "v". void setValue(const PIVariant & v); + + //! \~english Clear element and set it to \a PIJSON::Invalid. + //! \~russian Очищает элемент и устанавливает его в \a PIJSON::Invalid. void clear(); + + //! \~english Returns size of elements array if type is \a PIJSON::Array, size of elements map if type is \a PIJSON::Object, otherwise returns 0. + //! \~russian Возвращает размер массива элементов если тип \a PIJSON::Array, размер словаря элементов если тип \a PIJSON::Object, иначе возвращает 0. int size() const; + + //! \~english Returns if elements map contains key "key" if type is \a PIJSON::Object, otherwise returns \b false. + //! \~russian Возвращает содержит ли словарь элементов ключ "key" если тип \a PIJSON::Object, иначе возвращает \b false. bool contains(const PIString & key) const; + + //! \~english Set element type to \a PIJSON::Array and resize elements array to "new_size". + //! \~russian Устанавливает тип элемента в \a PIJSON::Array и изменяет размер массива элементов на "new_size". void resize(int new_size); - const PIJSON & operator[](int index) const; - PIJSON & operator[](int index); + + //! \~english Synonim of \a setValue(). + //! \~russian Аналог \a setValue(). PIJSON & operator=(const PIVariant & v) {setValue(v); return *this;} + PIJSON & operator=(const PIJSON & v); + + //! \~english Returns element from array with index "index" if type is \a PIJSON::Array, otherwise returns invalid %PIJSON. + //! \~russian Возвращает элемент из массива по индексу "index" если тип \a PIJSON::Array, иначе возвращает недействительный %PIJSON. + const PIJSON & operator[](int index) const; + + //! \~english Set element type to \a PIJSON::Array, resize if necessary and returns element from array with index "index". + //! \~russian Устанавливает тип элемента в \a PIJSON::Array, изменяет размер массива при неоходимости и возвращает элемент из массива по индексу "index". + PIJSON & operator[](int index); + + + //! \~english Returns element from map with key "key" if type is \a PIJSON::Object, otherwise returns invalid %PIJSON. + //! \~russian Возвращает элемент из словаря по ключу "key" если тип \a PIJSON::Object, иначе возвращает недействительный %PIJSON. PIJSON operator[](const PIString & key) const; + + //! \~english Set element type to \a PIJSON::Object and returns element from map with key "key". If element with this key doesn`t exists, it will be created. + //! \~russian Устанавливает тип элемента в \a PIJSON::Object и возвращает элемент из словаря по ключу "key". Если элемента с таким ключом не существует, он будет создан. 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; + + //! \~english Returns text representation of JSON tree. + //! \~russian Возвращает текстовое представление дерева JSON. + PIString toJSON(PrintType print_type = Tree) const; + + //! \~english Parse text representation of JSON "str" and returns it root element. + //! \~russian Разбирает текстовое представление JSON "str" и возвращает его корневой элемент. static PIJSON fromJSON(PIString str); + static PIJSON newObject(); + static PIJSON newArray(); + static PIJSON newString(const PIString & v = PIString()); + private: static PIJSON & nullEntry(); static PIString parseName(PIString & s); @@ -94,7 +183,7 @@ private: 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); + static void print(PIString & s, const PIJSON & v, PIString tab, bool spaces, bool transform = false, bool comma = false); PIString c_name; PIVariant c_value; @@ -112,7 +201,7 @@ inline PICout operator <<(PICout s, const PIJSON & v) { s.space(); s.saveAndSetControls(0); PIString str; - PIJSON::print(str, v, ""); + PIJSON::print(str, v, "", true); s << str; s.restoreControls(); return s; diff --git a/main.cpp b/main.cpp index b2db81f4..6d4651da 100644 --- a/main.cpp +++ b/main.cpp @@ -35,8 +35,8 @@ int main(int argc, char * argv[]) { s = PIString::fromUTF8("{\"st\\u0426r\":\"\\\\ \\\" \\u0425\\u0430\\n\"}"); //s = PIString::fromUTF8("{\"str\":\"Ха\"}"); PIJSON json = PIJSON::fromJSON(s); - piCout << json; - piCout << json.toJSON(); + //piCout << json; + //piCout << json.toJSON(); //json.resize(3); //json["0"].setValue(123); //json["1"].setValue("sec"); @@ -44,19 +44,21 @@ int main(int argc, char * argv[]) { //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); + json = PIJSON(); + json[0] = 123; + json[1] = PIString::fromUTF8("string русский хаха !"); + json[2]["b"] = true; + json[2]["i"] = -1; + auto & arr(json[3]); arr[0] = 10; arr[1] = 11.2E-1; arr[2] = "!!!"; - json["obj2"] = json["obj"]; + json[5] = json[2]; + json[7] = false; //piCout << json; - piCout << json.toJSON(); - piCout << json["str__"].toString().toUTF8(); + piCout << json; + piCout << json.toJSON(PIJSON::Tree); + piCout << json.toJSON(PIJSON::Compact); + //piCout << json["str__"].toString().toUTF8(); return 0; }