diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/scheme/scimpl_json_read.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/scheme/scimpl_json_read.cpp')
-rw-r--r-- | library/cpp/scheme/scimpl_json_read.cpp | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/library/cpp/scheme/scimpl_json_read.cpp b/library/cpp/scheme/scimpl_json_read.cpp new file mode 100644 index 0000000000..8a29cc7739 --- /dev/null +++ b/library/cpp/scheme/scimpl_json_read.cpp @@ -0,0 +1,229 @@ +#include "scimpl.h" + +#include <library/cpp/json/json_reader.h> +#include <util/stream/output.h> +#include <util/generic/maybe.h> + +namespace NSc { + struct TJsonError { + size_t Offset = 0; + TMaybe<TString> Reason; + }; + + struct TJsonDeserializer : NJson::TJsonCallbacks { + struct TContainer { + TValue* Container = nullptr; + TValue* LastValue = nullptr; + bool ExpectKey = false; + + TContainer(TValue& v) + : Container(&v) + , ExpectKey(v.IsDict()) + { + } + + bool Add(TStringBuf v, bool allowDuplicated) { + if (!ExpectKey || Y_UNLIKELY(!Container->IsDict())) + return false; + + if (!allowDuplicated && Y_UNLIKELY(Container->Has(v))) + return false; + + LastValue = &Container->GetOrAdd(v); + ExpectKey = false; + return true; + } + + void Push() { + LastValue = &Container->Push(); + } + + TValue& NextValue() { + if (Container->IsArray()) { + Push(); + } else if (Container->IsDict()) { + ExpectKey = true; + } + + return *(LastValue ? LastValue : Container); + } + + bool IsArray() const { + return !!Container && Container->IsArray(); + } + + bool IsDict() const { + return !!Container && Container->IsDict(); + } + }; + + typedef TVector<TContainer> TStackType; + + public: + TValue& Root; + TJsonError& Error; + const TJsonOpts& Cfg; + + TStackType Stack; + bool Virgin = true; + + public: + TJsonDeserializer(TValue& root, TJsonError& err, const TJsonOpts& cfg) + : Root(root) + , Error(err) + , Cfg(cfg) + { + Root.SetNull(); + Stack.reserve(10); + } + + bool HasNextValue() const { + return Virgin | !Stack.empty(); + } + + TValue& NextValue() { + Virgin = false; + return Stack.empty() ? Root : Stack.back().NextValue(); + } + + bool OnNull() override { + if (Y_UNLIKELY(!HasNextValue())) + return false; + + NextValue().SetNull(); + return true; + } + + bool OnEnd() override { + return Stack.empty(); + } + + template <typename T> + bool OnValue(T v) { + if (Y_UNLIKELY(!HasNextValue())) + return false; + + NextValue() = v; + return true; + } + + template <typename T> + bool OnIntValue(T v) { + if (Y_UNLIKELY(!HasNextValue())) + return false; + + NextValue().SetIntNumber(v); + return true; + } + + bool OnBoolean(bool v) override { + if (Y_UNLIKELY(!HasNextValue())) + return false; + + NextValue().SetBool(v); + return true; + } + + bool OnInteger(long long v) override { + return OnIntValue(v); + } + + bool OnUInteger(unsigned long long v) override { + return OnIntValue(v); + } + + bool OnDouble(double v) override { + return OnValue(v); + } + + bool OnString(const TStringBuf& v) override { + return OnValue(v); + } + + bool OnMapKey(const TStringBuf& k) override { + if (Y_UNLIKELY(Stack.empty())) + return false; + return Stack.back().Add(k, !(Cfg.Opts & TJsonOpts::JO_PARSER_DISALLOW_DUPLICATE_KEYS)); + } + + bool OnOpenMap() override { + if (Y_UNLIKELY(!HasNextValue())) + return false; + Stack.push_back(TContainer(NextValue().SetDict())); + return true; + } + + bool OnCloseMap() override { + if (Y_UNLIKELY(Stack.empty() || !Stack.back().IsDict())) + return false; + Stack.pop_back(); + return true; + } + + bool OnOpenArray() override { + if (Y_UNLIKELY(!HasNextValue())) + return false; + Stack.push_back(TContainer(NextValue().SetArray())); + return true; + } + + bool OnCloseArray() override { + if (Y_UNLIKELY(Stack.empty() || !Stack.back().IsArray())) + return false; + Stack.pop_back(); + return true; + } + + void OnError(size_t off, TStringBuf reason) override { + Error.Offset = off; + Error.Reason = reason; + } + }; + + static bool DoParseFromJson(TValue& res, TJsonError& err, TStringBuf json, const TJsonOpts& cfg) { + TJsonDeserializer d(res, err, cfg); + + if (cfg.RelaxedJson) { + return NJson::ReadJsonFast(json, &d); + } else { + TMemoryInput min(json.data(), json.size()); + return NJson::ReadJson(&min, &cfg, &d); + } + } + + static bool DoParseFromJson(TValue& res, TStringBuf json, const TJsonOpts& cfg) { + TJsonError err; + return DoParseFromJson(res, err, json, cfg); + } + + TValue TValue::FromJson(TStringBuf v, const TJsonOpts& cfg) { + TValue res; + if (FromJson(res, v, cfg)) { + return res; + } else { + return DefaultValue(); + } + } + + TValue TValue::FromJsonThrow(TStringBuf json, const TJsonOpts& cfg) { + TValue res; + TJsonError err; + + if (DoParseFromJson(res, err, json, cfg)) { + return res; + } + + TString reason = err.Reason.Empty() ? "NULL" : *err.Reason; + ythrow TSchemeParseException(err.Offset, reason) << "JSON error at offset " << err.Offset << " (" << reason << ")"; + } + + bool TValue::FromJson(TValue& res, TStringBuf json, const TJsonOpts& cfg) { + if (DoParseFromJson(res, json, cfg)) { + return true; + } + + res.SetNull(); + return false; + } + +} |