#include "json_value_ordered.h" #include "json_ordered.h" #include #include #include #include #include #include #include #include #include #include #include #include static bool AreJsonMapsEqual(const NJson::NOrderedJson::TJsonValue& lhs, const NJson::NOrderedJson::TJsonValue& rhs) { using namespace NJson::NOrderedJson; Y_ABORT_UNLESS(lhs.GetType() == JSON_MAP, "lhs has not a JSON_MAP type."); if (rhs.GetType() != JSON_MAP) { return false; } typedef TJsonValue::TMapType TMapType; const TMapType& lhsMap = lhs.GetMap(); const TMapType& rhsMap = rhs.GetMap(); if (lhsMap.size() != rhsMap.size()) { return false; } for (const auto& lhsIt : lhsMap) { TMapType::const_iterator rhsIt = rhsMap.find(lhsIt.first); if (rhsIt == rhsMap.end()) { return false; } if (lhsIt.second != rhsIt->second) { return false; } } return true; } static bool AreJsonArraysEqual(const NJson::NOrderedJson::TJsonValue& lhs, const NJson::NOrderedJson::TJsonValue& rhs) { using namespace NJson::NOrderedJson; Y_ABORT_UNLESS(lhs.GetType() == JSON_ARRAY, "lhs has not a JSON_ARRAY type."); if (rhs.GetType() != JSON_ARRAY) { return false; } typedef TJsonValue::TArray TArray; const TArray& lhsArray = lhs.GetArray(); const TArray& rhsArray = rhs.GetArray(); if (lhsArray.size() != rhsArray.size()) { return false; } for (TArray::const_iterator lhsIt = lhsArray.begin(), rhsIt = rhsArray.begin(); lhsIt != lhsArray.end(); ++lhsIt, ++rhsIt) { if (*lhsIt != *rhsIt) { return false; } } return true; } namespace NJson::NOrderedJson { const TJsonValue TJsonValue::UNDEFINED{}; TJsonValue::TJsonValue(const EJsonValueType type) { SetType(type); } TJsonValue::TJsonValue(TJsonValue&& vval) noexcept : Type(JSON_UNDEFINED) { vval.SwapWithUndefined(*this); Zero(vval.Value); } TJsonValue::TJsonValue(const NJson::TJsonValue& vval) { switch (vval.GetType()) { case NJson::EJsonValueType::JSON_ARRAY: SetType(JSON_ARRAY); for (const auto& item : vval.GetArray()) { AppendValue(TJsonValue(item)); } break; case NJson::EJsonValueType::JSON_MAP: SetType(JSON_MAP); for (const auto& item : vval.GetMap()) { InsertValue(item.first, TJsonValue(item.second)); } break; case NJson::EJsonValueType::JSON_BOOLEAN: SetType(JSON_BOOLEAN); Value.Boolean = vval.GetBoolean(); break; case NJson::EJsonValueType::JSON_INTEGER: SetType(JSON_INTEGER); Value.Integer = vval.GetInteger(); break; case NJson::EJsonValueType::JSON_UINTEGER: SetType(JSON_UINTEGER); Value.UInteger = vval.GetUInteger(); break; case NJson::EJsonValueType::JSON_STRING: SetType(JSON_STRING); Value.String = vval.GetString(); break; case NJson::EJsonValueType::JSON_NULL: SetType(JSON_NULL); break; case NJson::EJsonValueType::JSON_UNDEFINED: SetType(JSON_UNDEFINED); break; case NJson::EJsonValueType::JSON_DOUBLE: SetType(JSON_DOUBLE); Value.Double = vval.GetDouble(); break; } } TJsonValue::TJsonValue(const TJsonValue& val) : Type(val.Type) { switch (Type) { case JSON_STRING: new (&Value.String) TString(val.GetString()); break; case JSON_MAP: Value.Map = new TMapType(val.GetMap()); break; case JSON_ARRAY: Value.Array = new TArray(val.GetArray()); break; case JSON_UNDEFINED: case JSON_NULL: case JSON_BOOLEAN: case JSON_INTEGER: case JSON_UINTEGER: case JSON_DOUBLE: std::memcpy(&Value, &val.Value, sizeof(Value)); break; } } TJsonValue& TJsonValue::operator=(const TJsonValue& val) { if (this == &val) { return *this; } TJsonValue tmp(val); tmp.Swap(*this); return *this; } TJsonValue& TJsonValue::operator=(TJsonValue&& val) noexcept { if (this == &val) { return *this; } TJsonValue tmp(std::move(val)); tmp.Swap(*this); return *this; } TJsonValue::TJsonValue(const bool value) noexcept { SetType(JSON_BOOLEAN); Value.Boolean = value; } TJsonValue::TJsonValue(const long long value) noexcept { SetType(JSON_INTEGER); Value.Integer = value; } TJsonValue::TJsonValue(const unsigned long long value) noexcept { SetType(JSON_UINTEGER); Value.UInteger = value; } TJsonValue::TJsonValue(const int value) noexcept { SetType(JSON_INTEGER); Value.Integer = value; } TJsonValue::TJsonValue(const unsigned int value) noexcept { SetType(JSON_UINTEGER); Value.UInteger = value; } TJsonValue::TJsonValue(const long value) noexcept { SetType(JSON_INTEGER); Value.Integer = value; } TJsonValue::TJsonValue(const unsigned long value) noexcept { SetType(JSON_UINTEGER); Value.UInteger = value; } TJsonValue::TJsonValue(const double value) noexcept { SetType(JSON_DOUBLE); Value.Double = value; } TJsonValue::TJsonValue(TString value) { SetType(JSON_STRING); Value.String = std::move(value); } TJsonValue::TJsonValue(const TStringBuf value) { SetType(JSON_STRING); Value.String = value; } TJsonValue::TJsonValue(const char* value) { SetType(JSON_STRING); Value.String = value; } EJsonValueType TJsonValue::GetType() const noexcept { return Type; } TJsonValue& TJsonValue::SetType(const EJsonValueType type) { if (Type == type) { return *this; } Clear(); Type = type; switch (Type) { case JSON_STRING: new (&Value.String) TString(); break; case JSON_MAP: Value.Map = new TMapType(); break; case JSON_ARRAY: Value.Array = new TArray(); break; case JSON_UNDEFINED: case JSON_NULL: case JSON_BOOLEAN: case JSON_INTEGER: case JSON_UINTEGER: case JSON_DOUBLE: break; } return *this; } TJsonValue& TJsonValue::SetValue(const TJsonValue& value) { return *this = value; } TJsonValue& TJsonValue::SetValue(TJsonValue&& value) { *this = std::move(value); return *this; } TJsonValue& TJsonValue::InsertValue(const TString& key, const TJsonValue& value) { SetType(JSON_MAP); return (*Value.Map)[key] = value; } TJsonValue& TJsonValue::InsertValue(const TStringBuf key, const TJsonValue& value) { SetType(JSON_MAP); return (*Value.Map)[key] = value; } TJsonValue& TJsonValue::InsertValue(const char* key, const TJsonValue& value) { SetType(JSON_MAP); return (*Value.Map)[key] = value; } TJsonValue& TJsonValue::InsertValue(const TString& key, TJsonValue&& value) { SetType(JSON_MAP); return (*Value.Map)[key] = std::move(value); } TJsonValue& TJsonValue::InsertValue(const TStringBuf key, TJsonValue&& value) { SetType(JSON_MAP); return (*Value.Map)[key] = std::move(value); } TJsonValue& TJsonValue::InsertValue(const char* key, TJsonValue&& value) { SetType(JSON_MAP); return (*Value.Map)[key] = std::move(value); } TJsonValue& TJsonValue::Back() { BackChecks(); return Value.Array->back(); } const TJsonValue& TJsonValue::Back() const { BackChecks(); return Value.Array->back(); } TJsonValue& TJsonValue::AppendValue(const TJsonValue& value) { SetType(JSON_ARRAY); Value.Array->push_back(value); return Value.Array->back(); } TJsonValue& TJsonValue::AppendValue(TJsonValue&& value) { SetType(JSON_ARRAY); Value.Array->push_back(std::move(value)); return Value.Array->back(); } void TJsonValue::EraseValue(const TStringBuf key) { if (IsMap()) { TMapType::iterator it = Value.Map->find(key); if (it != Value.Map->end()) { Value.Map->erase(it); } } } void TJsonValue::EraseValue(const size_t index) { if (IsArray()) { if (index >= Value.Array->size()) { return; } TArray::iterator it = Value.Array->begin() + index; Value.Array->erase(it); } } void TJsonValue::Clear() noexcept { switch (Type) { case JSON_STRING: Value.String.~TString(); break; case JSON_MAP: delete Value.Map; break; case JSON_ARRAY: delete Value.Array; break; case JSON_UNDEFINED: case JSON_NULL: case JSON_BOOLEAN: case JSON_INTEGER: case JSON_UINTEGER: case JSON_DOUBLE: break; } Zero(Value); Type = JSON_UNDEFINED; } TJsonValue& TJsonValue::operator[](const size_t idx) { SetType(JSON_ARRAY); if (Value.Array->size() <= idx) { Value.Array->resize(idx + 1); } return (*Value.Array)[idx]; } TJsonValue& TJsonValue::operator[](const TStringBuf& key) { SetType(JSON_MAP); return (*Value.Map)[key]; } namespace { struct TDefaultsHolder { const TString String{}; const TJsonValue::TMapType Map{}; const TJsonValue::TArray Array{}; const TJsonValue Value{}; }; } // namespace const TJsonValue& TJsonValue::operator[](const size_t idx) const noexcept { const TJsonValue* ret = nullptr; if (GetValuePointer(idx, &ret)) { return *ret; } return Singleton()->Value; } const TJsonValue& TJsonValue::operator[](const TStringBuf& key) const noexcept { const TJsonValue* ret = nullptr; if (GetValuePointer(key, &ret)) { return *ret; } return Singleton()->Value; } bool TJsonValue::GetBoolean() const { return Type != JSON_BOOLEAN ? false : Value.Boolean; } long long TJsonValue::GetInteger() const { if (!IsInteger()) { return 0; } switch (Type) { case JSON_INTEGER: return Value.Integer; case JSON_UINTEGER: return Value.UInteger; case JSON_DOUBLE: return Value.Double; default: Y_ASSERT(false && "Unexpected type."); return 0; } } unsigned long long TJsonValue::GetUInteger() const { if (!IsUInteger()) { return 0; } switch (Type) { case JSON_UINTEGER: return Value.UInteger; case JSON_INTEGER: return Value.Integer; case JSON_DOUBLE: return Value.Double; default: Y_ASSERT(false && "Unexpected type."); return 0; } } double TJsonValue::GetDouble() const { if (!IsDouble()) { return 0.0; } switch (Type) { case JSON_DOUBLE: return Value.Double; case JSON_INTEGER: return Value.Integer; case JSON_UINTEGER: return Value.UInteger; default: Y_ASSERT(false && "Unexpected type."); return 0.0; } } const TString& TJsonValue::GetString() const { return Type != JSON_STRING ? Singleton()->String : Value.String; } const TJsonValue::TMapType& TJsonValue::GetMap() const { return Type != JSON_MAP ? Singleton()->Map : *Value.Map; } const TJsonValue::TArray& TJsonValue::GetArray() const { return (Type != JSON_ARRAY) ? Singleton()->Array : *Value.Array; } bool TJsonValue::GetBooleanSafe() const { if (Type != JSON_BOOLEAN) { ythrow TJsonException() << "Not a boolean"; } return Value.Boolean; } long long TJsonValue::GetIntegerSafe() const { if (!IsInteger()) { ythrow TJsonException() << "Not an integer"; } return GetInteger(); } unsigned long long TJsonValue::GetUIntegerSafe() const { if (!IsUInteger()) { ythrow TJsonException() << "Not an unsigned integer"; } return GetUInteger(); } double TJsonValue::GetDoubleSafe() const { if (!IsDouble()) { ythrow TJsonException() << "Not a double"; } return GetDouble(); } const TString& TJsonValue::GetStringSafe() const { if (Type != JSON_STRING) { ythrow TJsonException() << "Not a string"; } return Value.String; } bool TJsonValue::GetBooleanSafe(const bool defaultValue) const { if (Type == JSON_UNDEFINED) { return defaultValue; } return GetBooleanSafe(); } long long TJsonValue::GetIntegerSafe(const long long defaultValue) const { if (Type == JSON_UNDEFINED) { return defaultValue; } return GetIntegerSafe(); } unsigned long long TJsonValue::GetUIntegerSafe(const unsigned long long defaultValue) const { if (Type == JSON_UNDEFINED) { return defaultValue; } return GetUIntegerSafe(); } double TJsonValue::GetDoubleSafe(const double defaultValue) const { if (Type == JSON_UNDEFINED) { return defaultValue; } return GetDoubleSafe(); } TString TJsonValue::GetStringSafe(const TString& defaultValue) const { if (Type == JSON_UNDEFINED) { return defaultValue; } return GetStringSafe(); } const TJsonValue::TMapType& TJsonValue::GetMapSafe() const { if (Type != JSON_MAP) { ythrow TJsonException() << "Not a map"; } return *Value.Map; } TJsonValue::TMapType& TJsonValue::GetMapSafe() { return const_cast(const_cast(this)->GetMapSafe()); } const TJsonValue::TArray& TJsonValue::GetArraySafe() const { if (Type != JSON_ARRAY) { ythrow TJsonException() << "Not an array"; } return *Value.Array; } TJsonValue::TArray& TJsonValue::GetArraySafe() { return const_cast(const_cast(this)->GetArraySafe()); } bool TJsonValue::GetBooleanRobust() const noexcept { switch (Type) { case JSON_ARRAY: return !Value.Array->empty(); case JSON_MAP: return !Value.Map->empty(); case JSON_INTEGER: case JSON_UINTEGER: case JSON_DOUBLE: return GetIntegerRobust(); case JSON_STRING: return GetIntegerRobust() || IsTrue(Value.String); case JSON_NULL: case JSON_UNDEFINED: default: return false; case JSON_BOOLEAN: return Value.Boolean; } } long long TJsonValue::GetIntegerRobust() const noexcept { switch (Type) { case JSON_ARRAY: return Value.Array->size(); case JSON_MAP: return Value.Map->size(); case JSON_BOOLEAN: return Value.Boolean; case JSON_DOUBLE: return GetDoubleRobust(); case JSON_STRING: try { i64 res = 0; if (Value.String && TryFromString(Value.String, res)) { return res; } } catch (const yexception&) { } return 0; case JSON_NULL: case JSON_UNDEFINED: default: return 0; case JSON_INTEGER: case JSON_UINTEGER: return Value.Integer; } } unsigned long long TJsonValue::GetUIntegerRobust() const noexcept { switch (Type) { case JSON_ARRAY: return Value.Array->size(); case JSON_MAP: return Value.Map->size(); case JSON_BOOLEAN: return Value.Boolean; case JSON_DOUBLE: return GetDoubleRobust(); case JSON_STRING: try { ui64 res = 0; if (Value.String && TryFromString(Value.String, res)) { return res; } } catch (const yexception&) { } return 0; case JSON_NULL: case JSON_UNDEFINED: default: return 0; case JSON_INTEGER: case JSON_UINTEGER: return Value.UInteger; } } double TJsonValue::GetDoubleRobust() const noexcept { switch (Type) { case JSON_ARRAY: return Value.Array->size(); case JSON_MAP: return Value.Map->size(); case JSON_BOOLEAN: return Value.Boolean; case JSON_INTEGER: return Value.Integer; case JSON_UINTEGER: return Value.UInteger; case JSON_STRING: try { double res = 0; if (Value.String && TryFromString(Value.String, res)) { return res; } } catch (const yexception&) { } return 0; case JSON_NULL: case JSON_UNDEFINED: default: return 0; case JSON_DOUBLE: return Value.Double; } } TString TJsonValue::GetStringRobust() const { switch (Type) { case JSON_ARRAY: case JSON_MAP: case JSON_BOOLEAN: case JSON_DOUBLE: case JSON_INTEGER: case JSON_UINTEGER: case JSON_NULL: case JSON_UNDEFINED: default: { NJsonOrderedWriter::TBuf sout; sout.WriteJsonValue(this); return sout.Str(); } case JSON_STRING: return Value.String; } } NJson::TJsonValue TJsonValue::GetNonOrderedJsonValue() const { NJson::TJsonValue res; switch (Type) { case JSON_ARRAY: res.SetType(NJson::EJsonValueType::JSON_ARRAY); for (const auto& item : *Value.Array) { res.AppendValue(item.GetNonOrderedJsonValue()); } return res; case JSON_MAP: res.SetType(NJson::EJsonValueType::JSON_MAP); for (const auto& item : *Value.Map) { res.InsertValue(item.first, item.second.GetNonOrderedJsonValue()); } return res; case JSON_BOOLEAN: return NJson::TJsonValue(Value.Boolean); case JSON_INTEGER: return NJson::TJsonValue(Value.Integer); case JSON_UINTEGER: return NJson::TJsonValue(Value.UInteger); case JSON_STRING: return NJson::TJsonValue(Value.String); case JSON_NULL: return NJson::TJsonValue(NJson::EJsonValueType::JSON_NULL); case JSON_UNDEFINED: return NJson::TJsonValue(); case JSON_DOUBLE: return NJson::TJsonValue(Value.Double); } } bool TJsonValue::GetBoolean(bool* value) const noexcept { if (Type != JSON_BOOLEAN) { return false; } *value = Value.Boolean; return true; } bool TJsonValue::GetInteger(long long* value) const noexcept { if (!IsInteger()) { return false; } *value = GetInteger(); return true; } bool TJsonValue::GetUInteger(unsigned long long* value) const noexcept { if (!IsUInteger()) { return false; } *value = GetUInteger(); return true; } bool TJsonValue::GetDouble(double* value) const noexcept { if (!IsDouble()) { return false; } *value = GetDouble(); return true; } bool TJsonValue::GetString(TString* value) const { if (Type != JSON_STRING) { return false; } *value = Value.String; return true; } bool TJsonValue::GetMap(TJsonValue::TMapType* value) const { if (Type != JSON_MAP) { return false; } *value = *Value.Map; return true; } bool TJsonValue::GetArray(TJsonValue::TArray* value) const { if (Type != JSON_ARRAY) { return false; } *value = *Value.Array; return true; } bool TJsonValue::GetMapPointer(const TJsonValue::TMapType** value) const noexcept { if (Type != JSON_MAP) { return false; } *value = Value.Map; return true; } bool TJsonValue::GetArrayPointer(const TJsonValue::TArray** value) const noexcept { if (Type != JSON_ARRAY) { return false; } *value = Value.Array; return true; } bool TJsonValue::GetValue(const size_t index, TJsonValue* value) const { const TJsonValue* tmp = nullptr; if (GetValuePointer(index, &tmp)) { *value = *tmp; return true; } return false; } bool TJsonValue::GetValue(const TStringBuf key, TJsonValue* value) const { const TJsonValue* tmp = nullptr; if (GetValuePointer(key, &tmp)) { *value = *tmp; return true; } return false; } bool TJsonValue::GetValuePointer(const size_t index, const TJsonValue** value) const noexcept { if (Type == JSON_ARRAY && index < Value.Array->size()) { *value = &(*Value.Array)[index]; return true; } return false; } bool TJsonValue::GetValuePointer(const TStringBuf key, const TJsonValue** value) const noexcept { if (Type == JSON_MAP) { const TMapType::const_iterator it = Value.Map->find(key); if (it != Value.Map->end()) { *value = &(it->second); return true; } } return false; } bool TJsonValue::GetValuePointer(const TStringBuf key, TJsonValue** value) noexcept { return static_cast(this)->GetValuePointer(key, const_cast(value)); } bool TJsonValue::IsNull() const noexcept { return Type == JSON_NULL; } bool TJsonValue::IsBoolean() const noexcept { return Type == JSON_BOOLEAN; } bool TJsonValue::IsInteger() const noexcept { switch (Type) { case JSON_INTEGER: return true; case JSON_UINTEGER: return (Value.UInteger <= static_cast(Max())); case JSON_DOUBLE: return ((long long)Value.Double == Value.Double); default: return false; } } bool TJsonValue::IsUInteger() const noexcept { switch (Type) { case JSON_UINTEGER: return true; case JSON_INTEGER: return (Value.Integer >= 0); case JSON_DOUBLE: return ((unsigned long long)Value.Double == Value.Double); default: return false; } } bool TJsonValue::IsDouble() const noexcept { // Check whether we can convert integer to floating-point // without precision loss. switch (Type) { case JSON_DOUBLE: return true; case JSON_INTEGER: return (1ll << std::numeric_limits::digits) >= Abs(Value.Integer); case JSON_UINTEGER: return (1ull << std::numeric_limits::digits) >= Value.UInteger; default: return false; } } namespace { template TPtr* CreateOrNullptr(TPtr* p, T key, std::true_type /*create*/) { return &(*p)[key]; } template TPtr* CreateOrNullptr(const TPtr* p, T key, std::false_type /*create*/) noexcept { const TPtr* const next = &(*p)[key]; return next->GetType() != JSON_UNDEFINED ? const_cast(next) : nullptr; } template TJsonPtr GetValuePtrByPath(TJsonPtr currentJson, TStringBuf path, char delimiter) noexcept(!Create) { static_assert( !(Create && std::is_const>::value), "TJsonPtr must be a `TJsonValue*` if `Create` is true"); constexpr std::integral_constant create_tag{}; while (!path.empty()) { i64 index = 0; const TStringBuf step = path.NextTok(delimiter); if (step.size() > 2 && *step.begin() == '[' && step.back() == ']' && TryFromString(step.substr(1, step.size() - 2), index)) { if (index < 0) { if constexpr (Create) { currentJson->SetType(JSON_ARRAY); TJsonArray::TArray& dst = currentJson->GetArraySafe(); while (i64(dst.size()) < -index) { dst.push_front({JSON_NULL}); } } index = i64(currentJson->GetArray().size()) - (-index); } if (index < 0) { return nullptr; } currentJson = CreateOrNullptr(currentJson, index, create_tag); } else if (Create && step == "[]") { if constexpr (Create) { currentJson = ¤tJson->AppendValue({}); } } else { currentJson = CreateOrNullptr(currentJson, step, create_tag); } if (!currentJson) { return nullptr; } } return currentJson; } } // anonymous namespace bool TJsonValue::GetValueByPath(const TStringBuf path, TJsonValue& result, char delimiter) const { const TJsonValue* const ptr = GetValuePtrByPath(this, path, delimiter); if (ptr) { result = *ptr; return true; } return false; } bool TJsonValue::SetValueByPath(const TStringBuf path, const TJsonValue& value, char delimiter) { TJsonValue* const ptr = GetValuePtrByPath(this, path, delimiter); if (ptr) { *ptr = value; return true; } return false; } bool TJsonValue::SetValueByPath(const TStringBuf path, TJsonValue&& value, char delimiter) { TJsonValue* const ptr = GetValuePtrByPath(this, path, delimiter); if (ptr) { *ptr = std::move(value); return true; } return false; } const TJsonValue* TJsonValue::GetValueByPath(const TStringBuf key, char delim) const noexcept { return GetValuePtrByPath(this, key, delim); } TJsonValue* TJsonValue::GetValueByPath(const TStringBuf key, char delim) noexcept { return GetValuePtrByPath(this, key, delim); } void TJsonValue::DoScan(const TString& path, TJsonValue* parent, IScanCallback& callback) { if (!callback.Do(path, parent, *this)) { return; } if (Type == JSON_MAP) { for (auto&& i : *Value.Map) { i.second.DoScan(!!path ? TString::Join(path, ".", i.first) : i.first, this, callback); } } else if (Type == JSON_ARRAY) { for (ui32 i = 0; i < Value.Array->size(); ++i) { (*Value.Array)[i].DoScan(TString::Join(path, "[", ToString(i), "]"), this, callback); } } } void TJsonValue::Scan(IScanCallback& callback) { DoScan("", nullptr, callback); } bool TJsonValue::IsString() const noexcept { return Type == JSON_STRING; } bool TJsonValue::IsMap() const noexcept { return Type == JSON_MAP; } bool TJsonValue::IsArray() const noexcept { return Type == JSON_ARRAY; } bool TJsonValue::Has(const TStringBuf& key) const noexcept { return Type == JSON_MAP && Value.Map->contains(key); } bool TJsonValue::Has(size_t key) const noexcept { return Type == JSON_ARRAY && Value.Array->size() > key; } bool TJsonValue::operator==(const TJsonValue& rhs) const { switch (Type) { case JSON_UNDEFINED: return (rhs.GetType() == JSON_UNDEFINED); case JSON_NULL: return rhs.IsNull(); case JSON_BOOLEAN: return (rhs.IsBoolean() && Value.Boolean == rhs.Value.Boolean); case JSON_INTEGER: return (rhs.IsInteger() && GetInteger() == rhs.GetInteger()); case JSON_UINTEGER: return (rhs.IsUInteger() && GetUInteger() == rhs.GetUInteger()); case JSON_STRING: return (rhs.IsString() && Value.String == rhs.Value.String); case JSON_DOUBLE: return (rhs.IsDouble() && fabs(GetDouble() - rhs.GetDouble()) <= FLT_EPSILON); case JSON_MAP: return AreJsonMapsEqual(*this, rhs); case JSON_ARRAY: return AreJsonArraysEqual(*this, rhs); default: Y_ASSERT(false && "Unknown type."); return false; } } void TJsonValue::SwapWithUndefined(TJsonValue& output) noexcept { if (Type == JSON_STRING) { static_assert(std::is_nothrow_move_constructible::value, "noexcept violation! Add some try {} catch (...) logic"); new (&output.Value.String) TString(std::move(Value.String)); Value.String.~TString(); } else { std::memcpy(&output.Value, &Value, sizeof(Value)); } output.Type = Type; Type = JSON_UNDEFINED; } void TJsonValue::Swap(TJsonValue& rhs) noexcept { TJsonValue tmp(std::move(*this)); rhs.SwapWithUndefined(*this); tmp.SwapWithUndefined(rhs); } void TJsonValue::Save(IOutputStream* s) const { GetNonOrderedJsonValue().Save(s); } void TJsonValue::Load(IInputStream* s) { auto temp = GetNonOrderedJsonValue(); temp.Load(s); *this = TJsonValue(std::move(temp)); } //**************************************************************** bool GetMapPointer(const TJsonValue& jv, const size_t index, const TJsonValue::TMapType** value) { const TJsonValue* v; if (!jv.GetValuePointer(index, &v) || !v->IsMap()) { return false; } *value = &v->GetMap(); return true; } bool GetArrayPointer(const TJsonValue& jv, const size_t index, const TJsonValue::TArray** value) { const TJsonValue* v; if (!jv.GetValuePointer(index, &v) || !v->IsArray()) { return false; } *value = &v->GetArray(); return true; } bool GetMapPointer(const TJsonValue& jv, const TStringBuf key, const TJsonValue::TMapType** value) { const TJsonValue* v; if (!jv.GetValuePointer(key, &v) || !v->IsMap()) { return false; } *value = &v->GetMap(); return true; } bool GetArrayPointer(const TJsonValue& jv, const TStringBuf key, const TJsonValue::TArray** value) { const TJsonValue* v; if (!jv.GetValuePointer(key, &v) || !v->IsArray()) { return false; } *value = &v->GetArray(); return true; } void TJsonValue::BackChecks() const { if (Type != JSON_ARRAY) { ythrow TJsonException() << "Not an array"; } if (Value.Array->empty()) { ythrow TJsonException() << "Get back on empty array"; } } } // namespace NJson::NOrderedJson template <> void Out(IOutputStream& out, const NJson::NOrderedJson::TJsonValue& v) { NJsonOrderedWriter::TBuf buf(NJsonOrderedWriter::HEM_DONT_ESCAPE_HTML, &out); buf.WriteJsonValue(&v); }