aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/scheme/scimpl_json_read.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/scheme/scimpl_json_read.cpp
downloadydb-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.cpp229
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;
+ }
+
+}