#include "json_easy_parser.h"
#include <library/cpp/json/json_reader.h>
#include <util/string/cast.h>
#include <util/string/split.h>
#include <util/string/strip.h>
namespace NJson {
static TString MAP_IDENTIFIER = "{}";
static TString ARRAY_IDENTIFIER = "[]";
static TString ANY_IDENTIFIER = "*";
static void ParsePath(TString path, TVector<TPathElem>* res) {
TVector<const char*> parts;
Split(path.begin(), '/', &parts);
for (size_t n = 0; n < parts.size(); ++n) {
TString part = Strip(parts[n]);
if (!part.empty()) {
if (part[0] != '[') {
res->push_back(TPathElem(NImpl::MAP));
res->push_back(TPathElem(part));
} else {
int arrayCounter;
try {
arrayCounter = FromString<int>(part.substr(1, part.length() - 2));
} catch (yexception&) {
arrayCounter = -1;
}
res->push_back(TPathElem(arrayCounter));
}
}
}
}
void TJsonParser::AddField(const TString& path, bool nonEmpty) {
Fields.emplace_back();
Fields.back().NonEmpty = nonEmpty;
ParsePath(path, &Fields.back().Path);
}
TString TJsonParser::ConvertToTabDelimited(const TString& json) const {
TStringInput in(json);
TStringStream out;
ConvertToTabDelimited(in, out);
return out.Str();
}
class TRewriteJsonImpl: public NJson::TJsonCallbacks {
const TJsonParser& Parent;
TVector<TString> FieldValues;
TVector<TPathElem> Stack;
bool ShouldUpdateOnArrayChange;
int CurrentFieldIdx;
bool HasFormatError;
private:
static bool PathElementMatch(const TPathElem& templ, const TPathElem& real) {
if (templ.Type != real.Type)
return false;
if (templ.Type == NImpl::ARRAY)
return templ.ArrayCounter == -1 || templ.ArrayCounter == real.ArrayCounter;
if (templ.Type == NImpl::MAP_KEY)
return templ.Key == ANY_IDENTIFIER || templ.Key == real.Key;
return true;
}
bool CheckFilter(const TVector<TPathElem>& path) const {
if (Stack.size() < path.size())
return false;
for (size_t n = 0; n < path.size(); ++n) {
if (!PathElementMatch(path[n], Stack[n]))
return false;
}
return true;
}
void UpdateRule() {
for (size_t n = 0; n < Parent.Fields.size(); ++n) {
if (FieldValues[n].empty() && CheckFilter(Parent.Fields[n].Path)) {
CurrentFieldIdx = n;
return;
}
}
CurrentFieldIdx = -1;
}
void Pop() {
Stack.pop_back();
}
void IncreaseArrayCounter() {
if (!Stack.empty() && Stack.back().Type == NImpl::ARRAY) {
++Stack.back().ArrayCounter;
if (ShouldUpdateOnArrayChange)
UpdateRule();
}
}
template <class T>
bool OnValue(const T& val) {
IncreaseArrayCounter();
if (CurrentFieldIdx >= 0) {
FieldValues[CurrentFieldIdx] = ToString(val);
UpdateRule();
}
return true;
}
public:
TRewriteJsonImpl(const TJsonParser& parent)
: Parent(parent)
, FieldValues(parent.Fields.size())
, ShouldUpdateOnArrayChange(false)
, CurrentFieldIdx(-1)
, HasFormatError(false)
{
for (size_t n = 0; n < Parent.Fields.size(); ++n) {
if (!Parent.Fields[n].Path.empty() && Parent.Fields[n].Path.back().Type == NImpl::ARRAY)
ShouldUpdateOnArrayChange = true;
}
}
bool OnOpenMap() override {
IncreaseArrayCounter();
Stack.push_back(TPathElem(NImpl::MAP));
if (CurrentFieldIdx >= 0)
HasFormatError = true;
else
UpdateRule();
return true;
}
bool OnOpenArray() override {
IncreaseArrayCounter();
Stack.push_back(TPathElem(-1));
if (CurrentFieldIdx >= 0)
HasFormatError = true;
else
UpdateRule();
return true;
}
bool OnCloseMap() override {
while (!Stack.empty() && Stack.back().Type != NImpl::MAP)
Pop();
if (!Stack.empty())
Pop();
UpdateRule();
return true;
}
bool OnCloseArray() override {
if (!Stack.empty())
Pop();
UpdateRule();
return true;
}
bool OnMapKey(const TStringBuf& key) override {
if (!Stack.empty() && Stack.back().Type == NImpl::MAP_KEY) {
Pop();
UpdateRule();
}
Stack.push_back(TPathElem(TString{key}));
if (CurrentFieldIdx >= 0)
HasFormatError = true;
else
UpdateRule();
return true;
}
bool OnBoolean(bool b) override {
return OnValue(b);
}
bool OnInteger(long long i) override {
return OnValue(i);
}
bool OnDouble(double f) override {
return OnValue(f);
}
bool OnString(const TStringBuf& str) override {
return OnValue(str);
}
bool IsOK() const {
if (HasFormatError)
return false;
for (size_t n = 0; n < FieldValues.size(); ++n)
if (Parent.Fields[n].NonEmpty && FieldValues[n].empty())
return false;
return true;
}
void WriteTo(IOutputStream& out) const {
for (size_t n = 0; n < FieldValues.size(); ++n)
out << "\t" << FieldValues[n];
}
void WriteTo(TVector<TString>* res) const {
*res = FieldValues;
}
};
void TJsonParser::ConvertToTabDelimited(IInputStream& in, IOutputStream& out) const {
TRewriteJsonImpl impl(*this);
ReadJson(&in, &impl);
if (impl.IsOK()) {
out << Prefix;
impl.WriteTo(out);
out.Flush();
}
}
bool TJsonParser::Parse(const TString& json, TVector<TString>* res) const {
TRewriteJsonImpl impl(*this);
TStringInput in(json);
ReadJson(&in, &impl);
if (impl.IsOK()) {
impl.WriteTo(res);
return true;
} else
return false;
}
//struct TTestMe {
// TTestMe() {
// TJsonParser worker;
// worker.AddField("/x/y/z", true);
// TString ret1 = worker.ConvertToTabDelimited("{ \"x\" : { \"y\" : { \"w\" : 1, \"z\" : 2 } } }");
// TString ret2 = worker.ConvertToTabDelimited(" [1, 2, 3, 4, 5] ");
// }
//} testMe;
}