Files
pip/libs/main/serialization/pijsonserialization.h
2026-03-12 14:46:57 +03:00

565 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! \~\file pijsonserialization.h
//! \~\ingroup Serialization
//! \~\brief
//! \~english Generic JSON serialization helpers
//! \~russian Вспомогательные шаблоны для сериализации JSON
//! \details
//! \~english
//! This file provides template functions for serializing and deserializing various types to and from PIJSON format.
//! \~russian
//! Этот файл предоставляет шаблонные функции для сериализации и десериализации различных типов в формат PIJSON и из него.
/*
PIP - Platform Independent Primitives
Generic JSON serialization helpers
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/>.
*/
#ifndef PIJSONSERIALIZATION_H
#define PIJSONSERIALIZATION_H
#include "pijson.h"
// ---
// serialize, PIJSON = piSerializeJSON(T)
// ---
// enum & arithmetic
//! \~english Serializes enum values as JSON numbers.
//! \~russian Сериализует значения перечислений как JSON-числа.
template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
inline PIJSON piSerializeJSON(const T & v) {
return PIJSON() = (int)v;
}
//! \~english Serializes arithmetic values as JSON scalars.
//! \~russian Сериализует арифметические значения как JSON-скаляры.
template<typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
inline PIJSON piSerializeJSON(const T & v) {
return PIJSON() = v;
}
//! \~english Fallback overload that intentionally fails for unsupported complex types.
//! \~russian Резервная перегрузка, которая намеренно завершает компиляцию ошибкой для неподдерживаемых сложных типов.
template<typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
inline PIJSON piSerializeJSON(const T & v) {
static_assert(std::is_enum<T>::value || std::is_arithmetic<T>::value,
"[piSerializeJSON] Error: using undeclared piSerializeJSON() for complex type!");
return {};
}
// known types
//! \~english Returns JSON node unchanged.
//! \~russian Возвращает JSON-узел без изменений.
inline PIJSON piSerializeJSON(const PIJSON & v) {
return v;
}
template<>
//! \~english Serializes %PIVariant as the matching scalar JSON node.
//! \~russian Сериализует %PIVariant в соответствующий скалярный JSON-узел.
inline PIJSON piSerializeJSON(const PIVariant & v) {
return PIJSON() = v;
}
template<typename T>
//! \~english Serializes complex number as array [real, imag].
//! \~russian Сериализует комплексное число как массив [real, imag].
inline PIJSON piSerializeJSON(const complex<T> & v) {
PIJSON ret;
ret << v.real() << v.imag();
return ret;
}
template<typename T>
//! \~english Serializes %PIFlags as an integer mask.
//! \~russian Сериализует %PIFlags как целочисленную маску.
inline PIJSON piSerializeJSON(const PIFlags<T> & v) {
return PIJSON() = (int)v;
}
template<>
//! \~english Serializes %PIString as JSON string.
//! \~russian Сериализует %PIString как JSON-строку.
inline PIJSON piSerializeJSON(const PIString & v) {
return PIJSON() = v;
}
template<>
//! \~english Serializes %PIConstChars as JSON string.
//! \~russian Сериализует %PIConstChars как JSON-строку.
inline PIJSON piSerializeJSON(const PIConstChars & v) {
return PIJSON() = v.toString();
}
template<>
//! \~english Serializes %PIByteArray as Base64 string.
//! \~russian Сериализует %PIByteArray как строку Base64.
inline PIJSON piSerializeJSON(const PIByteArray & v) {
return PIJSON() = PIStringAscii(v.toBase64());
}
template<>
//! \~english Serializes %PISystemTime as object with fields \c s and \c ns.
//! \~russian Сериализует %PISystemTime как объект с полями \c s и \c ns.
inline PIJSON piSerializeJSON(const PISystemTime & v) {
PIJSON ret;
ret["s"] = v.seconds;
ret["ns"] = v.nanoseconds;
return ret;
}
template<>
//! \~english Serializes %PITime as object with fields \c h, \c m, \c s and \c z.
//! \~russian Сериализует %PITime как объект с полями \c h, \c m, \c s и \c z.
inline PIJSON piSerializeJSON(const PITime & v) {
PIJSON ret;
ret["h"] = v.hours;
ret["m"] = v.minutes;
ret["s"] = v.seconds;
ret["z"] = v.milliseconds;
return ret;
}
template<>
//! \~english Serializes %PIDate as object with fields \c y, \c M and \c d.
//! \~russian Сериализует %PIDate как объект с полями \c y, \c M и \c d.
inline PIJSON piSerializeJSON(const PIDate & v) {
PIJSON ret;
ret["y"] = v.year;
ret["M"] = v.month;
ret["d"] = v.day;
return ret;
}
template<>
//! \~english Serializes %PIDateTime as object with fields \c y, \c M, \c d, \c h, \c m, \c s and \c z.
//! \~russian Сериализует %PIDateTime как объект с полями \c y, \c M, \c d, \c h, \c m, \c s и \c z.
inline PIJSON piSerializeJSON(const PIDateTime & v) {
PIJSON ret;
ret["y"] = v.year;
ret["M"] = v.month;
ret["d"] = v.day;
ret["h"] = v.hours;
ret["m"] = v.minutes;
ret["s"] = v.seconds;
ret["z"] = v.milliseconds;
return ret;
}
template<>
//! \~english Serializes %PINetworkAddress as string.
//! \~russian Сериализует %PINetworkAddress как строку.
inline PIJSON piSerializeJSON(const PINetworkAddress & v) {
return PIJSON() = v.toString();
}
template<typename T>
//! \~english Serializes %PIPoint as object with fields \c x and \c y.
//! \~russian Сериализует %PIPoint как объект с полями \c x и \c y.
inline PIJSON piSerializeJSON(const PIPoint<T> & v) {
PIJSON ret;
ret["x"] = piSerializeJSON(v.x);
ret["y"] = piSerializeJSON(v.y);
return ret;
}
template<typename T>
//! \~english Serializes %PILine as object with fields \c p0 and \c p1.
//! \~russian Сериализует %PILine как объект с полями \c p0 и \c p1.
inline PIJSON piSerializeJSON(const PILine<T> & v) {
PIJSON ret;
ret["p0"] = piSerializeJSON(v.p0);
ret["p1"] = piSerializeJSON(v.p1);
return ret;
}
template<typename T>
//! \~english Serializes %PIRect as object with fields \c bl and \c tr.
//! \~russian Сериализует %PIRect как объект с полями \c bl и \c tr.
inline PIJSON piSerializeJSON(const PIRect<T> & v) {
PIJSON ret;
ret["bl"] = piSerializeJSON(v.bottomLeft());
ret["tr"] = piSerializeJSON(v.topRigth());
return ret;
}
// containers
//! \~english Serializes %PIPair as two-element array.
//! \~russian Сериализует %PIPair как массив из двух элементов.
template<typename T1, typename T2>
inline PIJSON piSerializeJSON(const PIPair<T1, T2> & v) {
PIJSON ret;
ret << piSerializeJSON(v.first) << piSerializeJSON(v.second);
return ret;
}
template<typename T>
//! \~english Serializes %PIVector as JSON array.
//! \~russian Сериализует %PIVector как JSON-массив.
inline PIJSON piSerializeJSON(const PIVector<T> & v) {
if (v.isEmpty()) return PIJSON::newArray();
PIJSON ret;
for (const auto & i: v)
ret << piSerializeJSON(i);
return ret;
}
template<typename T>
//! \~english Serializes %PIDeque as JSON array.
//! \~russian Сериализует %PIDeque как JSON-массив.
inline PIJSON piSerializeJSON(const PIDeque<T> & v) {
if (v.isEmpty()) return PIJSON::newArray();
PIJSON ret;
for (const auto & i: v)
ret << piSerializeJSON(i);
return ret;
}
template<typename T>
//! \~english Serializes %PIVector2D as object with \c rows, \c cols and flattened \c mat.
//! \~russian Сериализует %PIVector2D как объект с \c rows, \c cols и плоским массивом \c mat.
inline PIJSON piSerializeJSON(const PIVector2D<T> & v) {
PIJSON ret;
ret["cols"] = static_cast<uint>(v.cols());
ret["rows"] = static_cast<uint>(v.rows());
ret["mat"] = piSerializeJSON(v.plainVector());
return ret;
}
template<typename T>
//! \~english Serializes %PISet as JSON array.
//! \~russian Сериализует %PISet как JSON-массив.
inline PIJSON piSerializeJSON(const PISet<T> & v) {
if (v.isEmpty()) return PIJSON::newArray();
PIJSON ret;
for (const auto & i: v)
ret << piSerializeJSON(i);
return ret;
}
template<typename K, typename T>
//! \~english Serializes %PIMap as JSON object with stringified keys.
//! \~russian Сериализует %PIMap как JSON-объект со строковыми ключами.
inline PIJSON piSerializeJSON(const PIMap<K, T> & v) {
if (v.isEmpty()) return PIJSON::newObject();
PIJSON ret;
auto it = v.makeIterator();
while (it.next())
ret[PIVariant(it.key()).toString()] = piSerializeJSON(it.value());
return ret;
}
template<typename T>
//! \~english Serializes %PIMathVector as JSON array.
//! \~russian Сериализует %PIMathVector как JSON-массив.
inline PIJSON piSerializeJSON(const PIMathVector<T> & v) {
PIJSON ret;
for (uint i = 0; i < v.size(); ++i)
ret << piSerializeJSON(v[i]);
return ret;
}
template<uint Size, typename T>
//! \~english Serializes fixed-size %PIMathVectorT as JSON array.
//! \~russian Сериализует %PIMathVectorT фиксированного размера как JSON-массив.
inline PIJSON piSerializeJSON(const PIMathVectorT<Size, T> & v) {
PIJSON ret;
for (uint i = 0; i < v.size(); ++i)
ret << piSerializeJSON(v[i]);
return ret;
}
// ---
// deserialize, piDeserializeJSON(T, PIJSON)
// ---
// enum & arithmetic
//! \~english Deserializes enum value from JSON number.
//! \~russian Десериализует значение перечисления из JSON-числа.
template<typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
inline void piDeserializeJSON(T & v, const PIJSON & js) {
v = (T)js.toInt();
}
//! \~english Deserializes arithmetic value from JSON scalar.
//! \~russian Десериализует арифметическое значение из JSON-скаляра.
template<typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<std::is_arithmetic<T>::value, int>::type = 0>
inline void piDeserializeJSON(T & v, const PIJSON & js) {
v = js.value().value<T>();
}
//! \~english Fallback overload that intentionally fails for unsupported complex types.
//! \~russian Резервная перегрузка, которая намеренно завершает компиляцию ошибкой для неподдерживаемых сложных типов.
template<typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type = 0>
inline void piDeserializeJSON(T & v, const PIJSON & js) {
static_assert(std::is_enum<T>::value || std::is_arithmetic<T>::value,
"[piDeserializeJSON] Error: using undeclared piDeserializeJSON() for complex type!");
v = {};
}
// known types
//! \~english Copies JSON node as-is.
//! \~russian Копирует JSON-узел без изменений.
inline void piDeserializeJSON(PIJSON & v, const PIJSON & js) {
v = js;
}
template<>
//! \~english Extracts scalar value from JSON node into %PIVariant.
//! \~russian Извлекает скалярное значение JSON-узла в %PIVariant.
inline void piDeserializeJSON(PIVariant & v, const PIJSON & js) {
v = js.value();
}
template<typename T>
//! \~english Deserializes complex number from array [real, imag].
//! \~russian Десериализует комплексное число из массива [real, imag].
inline void piDeserializeJSON(complex<T> & v, const PIJSON & js) {
if (!js.isArray()) return;
piDeserializeJSON(reinterpret_cast<T(&)[2]>(v)[0], js[0]);
piDeserializeJSON(reinterpret_cast<T(&)[2]>(v)[1], js[1]);
}
template<typename T>
//! \~english Deserializes %PIFlags from integer mask.
//! \~russian Десериализует %PIFlags из целочисленной маски.
inline void piDeserializeJSON(PIFlags<T> & v, const PIJSON & js) {
v = js.toInt();
}
template<>
//! \~english Deserializes %PIString from JSON string representation.
//! \~russian Десериализует %PIString из строкового представления JSON.
inline void piDeserializeJSON(PIString & v, const PIJSON & js) {
v = js.toString();
}
template<>
//! \~english Deserializes %PIByteArray from Base64 string.
//! \~russian Десериализует %PIByteArray из строки Base64.
inline void piDeserializeJSON(PIByteArray & v, const PIJSON & js) {
v = PIByteArray::fromBase64(js.toString());
}
template<>
//! \~english Deserializes %PISystemTime from object with fields \c s and \c ns.
//! \~russian Десериализует %PISystemTime из объекта с полями \c s и \c ns.
inline void piDeserializeJSON(PISystemTime & v, const PIJSON & js) {
piDeserializeJSON(v.seconds, js["s"]);
piDeserializeJSON(v.nanoseconds, js["ns"]);
}
template<>
//! \~english Deserializes %PITime from object with fields \c h, \c m, \c s and \c z.
//! \~russian Десериализует %PITime из объекта с полями \c h, \c m, \c s и \c z.
inline void piDeserializeJSON(PITime & v, const PIJSON & js) {
v.hours = js["h"].toInt();
v.minutes = js["m"].toInt();
v.seconds = js["s"].toInt();
v.milliseconds = js["z"].toInt();
}
template<>
//! \~english Deserializes %PIDate from object with fields \c y, \c M and \c d.
//! \~russian Десериализует %PIDate из объекта с полями \c y, \c M и \c d.
inline void piDeserializeJSON(PIDate & v, const PIJSON & js) {
v.year = js["y"].toInt();
v.month = js["M"].toInt();
v.day = js["d"].toInt();
}
template<>
//! \~english Deserializes %PIDateTime from object with fields \c y, \c M, \c d, \c h, \c m, \c s and \c z.
//! \~russian Десериализует %PIDateTime из объекта с полями \c y, \c M, \c d, \c h, \c m, \c s и \c z.
inline void piDeserializeJSON(PIDateTime & v, const PIJSON & js) {
v.year = js["y"].toInt();
v.month = js["M"].toInt();
v.day = js["d"].toInt();
v.hours = js["h"].toInt();
v.minutes = js["m"].toInt();
v.seconds = js["s"].toInt();
v.milliseconds = js["z"].toInt();
}
template<>
//! \~english Deserializes %PINetworkAddress from string.
//! \~russian Десериализует %PINetworkAddress из строки.
inline void piDeserializeJSON(PINetworkAddress & v, const PIJSON & js) {
v = PINetworkAddress(js.toString());
}
template<typename T>
//! \~english Deserializes %PIPoint from object with fields \c x and \c y.
//! \~russian Десериализует %PIPoint из объекта с полями \c x и \c y.
inline void piDeserializeJSON(PIPoint<T> & v, const PIJSON & js) {
piDeserializeJSON(v.x, js["x"]);
piDeserializeJSON(v.y, js["y"]);
}
template<typename T>
//! \~english Deserializes %PILine from object with fields \c p0 and \c p1.
//! \~russian Десериализует %PILine из объекта с полями \c p0 и \c p1.
inline void piDeserializeJSON(PILine<T> & v, const PIJSON & js) {
piDeserializeJSON(v.p0, js["p0"]);
piDeserializeJSON(v.p1, js["p1"]);
}
template<typename T>
//! \~english Deserializes %PIRect from object with fields \c bl and \c tr.
//! \~russian Десериализует %PIRect из объекта с полями \c bl и \c tr.
inline void piDeserializeJSON(PIRect<T> & v, const PIJSON & js) {
PIPoint<T> bl, tr;
piDeserializeJSON(bl, js["bl"]);
piDeserializeJSON(tr, js["tr"]);
v = PIRect<T>(bl, tr);
}
// containers
//! \~english Deserializes %PIPair from two-element array.
//! \~russian Десериализует %PIPair из массива из двух элементов.
template<typename T1, typename T2>
inline void piDeserializeJSON(PIPair<T1, T2> & v, const PIJSON & js) {
v = {};
if (!js.isArray()) return;
piDeserializeJSON(v.first, js[0]);
piDeserializeJSON(v.second, js[1]);
}
template<typename T>
//! \~english Deserializes %PIVector from JSON array.
//! \~russian Десериализует %PIVector из JSON-массива.
inline void piDeserializeJSON(PIVector<T> & v, const PIJSON & js) {
v.clear();
if (!js.isArray()) return;
v.resize(js.size());
for (int i = 0; i < js.size(); ++i)
piDeserializeJSON(v[i], js[i]);
}
template<typename T>
//! \~english Deserializes %PIDeque from JSON array.
//! \~russian Десериализует %PIDeque из JSON-массива.
inline void piDeserializeJSON(PIDeque<T> & v, const PIJSON & js) {
v.clear();
if (!js.isArray()) return;
v.resize(js.size());
for (int i = 0; i < js.size(); ++i)
piDeserializeJSON(v[i], js[i]);
}
template<typename T>
//! \~english Deserializes %PIVector2D from object with \c rows, \c cols and flattened \c mat.
//! \~russian Десериализует %PIVector2D из объекта с \c rows, \c cols и плоским массивом \c mat.
inline void piDeserializeJSON(PIVector2D<T> & v, const PIJSON & js) {
v.clear();
if (!js.isObject()) return;
const auto & mat(js["mat"]);
if (!mat.isArray()) return;
piDeserializeJSON(v.plainVector(), mat);
v.resize(js["rows"].toInt(), js["cols"].toInt());
}
template<typename T>
//! \~english Deserializes %PISet from JSON array.
//! \~russian Десериализует %PISet из JSON-массива.
inline void piDeserializeJSON(PISet<T> & v, const PIJSON & js) {
v.clear();
if (!js.isArray()) return;
T _t;
for (int i = 0; i < js.size(); ++i) {
piDeserializeJSON(_t, js[i]);
v << _t;
}
}
template<typename K, typename T>
//! \~english Deserializes %PIMap from JSON object with stringified keys.
//! \~russian Десериализует %PIMap из JSON-объекта со строковыми ключами.
inline void piDeserializeJSON(PIMap<K, T> & v, const PIJSON & js) {
v.clear();
if (!js.isObject()) return;
const auto & obj(js.object());
auto it = obj.makeIterator();
while (it.next())
piDeserializeJSON(v[PIVariant(it.key()).value<K>()], it.value());
}
template<typename T>
//! \~english Deserializes dynamic %PIMathVector from JSON array.
//! \~russian Десериализует динамический %PIMathVector из JSON-массива.
inline void piDeserializeJSON(PIMathVector<T> & v, const PIJSON & js) {
v = {};
if (!js.isArray()) return;
v = PIMathVector<T>(js.size());
for (int i = 0; i < js.size(); ++i)
piDeserializeJSON(v[i], js[i]);
}
template<uint Size, typename T>
//! \~english Deserializes fixed-size %PIMathVectorT from JSON array, truncating extra items.
//! \~russian Десериализует %PIMathVectorT фиксированного размера из JSON-массива, отбрасывая лишние элементы.
inline void piDeserializeJSON(PIMathVectorT<Size, T> & v, const PIJSON & js) {
v = PIMathVectorT<Size, T>();
if (!js.isArray()) return;
int cnt = piMini(js.size(), Size);
for (int i = 0; i < cnt; ++i)
piDeserializeJSON(v[i], js[i]);
}
// ---
// PIJSON static wrapper
// ---
template<typename T>
//! \~english Convenience wrapper around \a piSerializeJSON().
//! \~russian Вспомогательная обертка над \a piSerializeJSON().
PIJSON PIJSON::serialize(const T & v) {
return piSerializeJSON(v);
}
template<typename T>
//! \~english Convenience wrapper around \a piDeserializeJSON().
//! \~russian Вспомогательная обертка над \a piDeserializeJSON().
T PIJSON::deserialize(const PIJSON & json) {
T ret;
piDeserializeJSON(ret, json);
return ret;
}
#endif // PIJSONSERIALIZATION_H