#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;
}
}