Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions universal/include/userver/formats/json/parser/array_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ namespace formats::json::parser {
template <typename Item, typename ItemParser, typename Array = std::vector<Item>>
class ArrayParser final : public TypedParser<Array>, public Subscriber<Item> {
public:
explicit ArrayParser(ItemParser& item_parser) : item_parser_(item_parser) { this->item_parser_.Subscribe(*this); }
explicit ArrayParser(ItemParser&& item_parser) : item_parser_(std::make_shared<ItemParser>(std::move(item_parser))) { }
explicit ArrayParser(ItemParser& item_parser) : item_parser_(std::make_shared<ItemParser>(item_parser)) { }

void Reset() override {
index_ = 0;
state_ = State::kStart;
storage_.clear();

this->item_parser_->Subscribe(*this);

if constexpr (meta::kIsVector<Array>) {
/*
* Heuristics:
Expand Down Expand Up @@ -88,8 +91,8 @@ class ArrayParser final : public TypedParser<Array>, public Subscriber<Item> {
this->Throw(std::string(what));
}

this->item_parser_.Reset();
this->parser_state_->PushParser(item_parser_.GetParser());
this->item_parser_->Reset();
this->parser_state_->PushParser(item_parser_->GetParser());
index_++;
}

Expand All @@ -103,10 +106,10 @@ class ArrayParser final : public TypedParser<Array>, public Subscriber<Item> {

std::string GetPathItem() const override { return common::GetIndexString(index_ - 1); }

BaseParser& Parser() { return item_parser_.GetParser(); }
BaseParser& Parser() { return item_parser_->GetParser(); }

private:
ItemParser& item_parser_;
std::shared_ptr<ItemParser> item_parser_;
std::optional<size_t> min_items_, max_items_;

enum class State {
Expand Down
11 changes: 6 additions & 5 deletions universal/include/userver/formats/json/parser/map_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class MapParser final : public TypedParser<Map>, public Subscriber<typename Map:
public:
using Value = typename Map::mapped_type;

explicit MapParser(ValueParser& value_parser) : value_parser_(value_parser) {}
explicit MapParser(ValueParser&& value_parser) : value_parser_(std::make_shared<ValueParser>(std::move(value_parser))) {}
explicit MapParser(ValueParser& value_parser) : value_parser_(std::make_shared<ValueParser>(value_parser)) {}

void Reset() override { this->state_ = State::kStart; }

Expand All @@ -30,9 +31,9 @@ class MapParser final : public TypedParser<Map>, public Subscriber<typename Map:
if (state_ != State::kInside) this->Throw("object");

key_ = key;
this->value_parser_.Reset();
this->value_parser_.Subscribe(*this);
this->parser_state_->PushParser(this->value_parser_.GetParser());
this->value_parser_->Reset();
this->value_parser_->Subscribe(*this);
this->parser_state_->PushParser(this->value_parser_->GetParser());
}

void EndObject() override {
Expand Down Expand Up @@ -68,7 +69,7 @@ class MapParser final : public TypedParser<Map>, public Subscriber<typename Map:
State state_;
std::string key_;
Map result_;
ValueParser& value_parser_;
std::shared_ptr<ValueParser> value_parser_;
};

} // namespace formats::json::parser
Expand Down
73 changes: 73 additions & 0 deletions universal/include/userver/formats/json/parser/meta_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#include <userver/formats/json/parser/parser.hpp>
#include <userver/formats/json/parser/parser_json.hpp>
#include <userver/formats/json/parser/parser_json_adapter.hpp>
#include <userver/utils/meta.hpp>

USERVER_NAMESPACE_BEGIN

namespace formats::json::parser {

template <typename T, typename Enable = void>
struct Parser;

template <typename ParserType>
struct ParserBase {
using type = ParserType;
static std::unique_ptr<type> Create() { return std::make_unique<type>(); }
};

template <>
struct Parser<bool> : ParserBase<BoolParser> {};

template <>
struct Parser<double> : ParserBase<DoubleParser> {};

template <>
struct Parser<int32_t> : ParserBase<Int32Parser> {};

template <>
struct Parser<int64_t> : ParserBase<Int64Parser> {};

template <>
struct Parser<std::string> : ParserBase<StringParser> {};

// for custom types
template <typename T>
struct Parser<T, std::enable_if_t<!meta::kIsRange<T> && !meta::kIsMap<T>>> : ParserBase<JsonValueAsAdapterParser<T>> {};

template <typename T>
struct Parser<T, std::enable_if_t<meta::kIsRange<T> && !meta::kIsMap<T>>> {
using ElementType = typename T::value_type;
using ElementParser = typename Parser<ElementType>::type;
using type = ArrayParser<ElementType, ElementParser, T>;

static std::unique_ptr<type> Create() {
auto element_parser = Parser<ElementType>::Create();
return std::make_unique<type>(std::move(*element_parser));
}
};

template <typename T>
struct Parser<T, std::enable_if_t<meta::kIsMap<T>>> {
static_assert(std::is_same_v<typename T::key_type, std::string>, "JSON object keys must be strings.");

using MappedType = typename T::mapped_type;
using MappedParser = typename Parser<MappedType>::type;
using type = MapParser<T, MappedParser>;

static std::unique_ptr<type> Create() {
auto mapped_parser = Parser<MappedType>::Create();
return std::make_unique<type>(std::move(*mapped_parser));
}
};

template <typename T>
auto CreateParser() {
return Parser<T>::Create();
}

} // namespace formats::json::parser

USERVER_NAMESPACE_END
1 change: 1 addition & 0 deletions universal/include/userver/formats/json/parser/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <userver/formats/json/parser/number_parser.hpp>
#include <userver/formats/json/parser/parser_json.hpp>
#include <userver/formats/json/parser/string_parser.hpp>
#include <userver/formats/json/parser/meta_parser.hpp>

USERVER_NAMESPACE_BEGIN

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ class JsonValueParser final : public TypedParser<Value> {
void EndArray(size_t) override;

std::string Expected() const override;
std::string GetPathItem() const override { return {}; }

private:
void MaybePopSelf();

std::string GetPathItem() const override { return {}; }

struct Impl;
utils::FastPimpl<Impl, 127, 8> impl_;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include <userver/formats/json/parser/parser_json.hpp>
#include <userver/formats/json/parser/typed_parser.hpp>
#include <userver/formats/json/value.hpp>

USERVER_NAMESPACE_BEGIN

namespace formats::json::parser {

template <typename T>
class JsonValueAsAdapterParser final : public Subscriber<Value> {
public:
using ResultType = T;

JsonValueAsAdapterParser() : value_parser_(std::make_unique<JsonValueParser>()) { value_parser_->Subscribe(*this); }

JsonValueAsAdapterParser(JsonValueAsAdapterParser&& other) noexcept
: value_parser_(std::move(other.value_parser_)), subscriber_(other.subscriber_) {
if (value_parser_) {
value_parser_->Subscribe(*this);
}
}

JsonValueAsAdapterParser& operator=(JsonValueAsAdapterParser&& other) noexcept {
if (this != &other) {
value_parser_ = std::move(other.value_parser_);
subscriber_ = other.subscriber_;
if (value_parser_) {
value_parser_->Subscribe(*this);
}
}
return *this;
}

JsonValueAsAdapterParser(const JsonValueAsAdapterParser&) = delete;
JsonValueAsAdapterParser& operator=(const JsonValueAsAdapterParser&) = delete;

void Reset() {
if (value_parser_) {
value_parser_->Reset();
}
}

void Subscribe(Subscriber<T>& subscriber) { subscriber_ = &subscriber; }

void OnSend(Value&& value) override {
if (subscriber_) {
try {
subscriber_->OnSend(value.As<T>());
} catch (const std::exception& e) {
throw InternalParseError(std::string("Failed to convert json value to type: ") + e.what());
}
}
}

operator BaseParser&() { return value_parser_->GetParser(); }

BaseParser& GetParser() { return value_parser_->GetParser(); }

private:
std::unique_ptr<JsonValueParser> value_parser_;
Subscriber<T>* subscriber_{nullptr};
};

} // namespace formats::json::parser

USERVER_NAMESPACE_END
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class ParserState final {

void ProcessInput(std::string_view sw);

void ProcessInput(std::istream& is);

void PopMe(BaseParser& parser);

[[noreturn]] void ThrowError(const std::string& err_msg);
Expand Down
16 changes: 16 additions & 0 deletions universal/include/userver/formats/json/parser/typed_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ typename Parser::ResultType ParseSingle(Parser& parser, std::string_view input)
return result;
}

template <typename Parser>
typename Parser::ResultType ParseSingle(Parser& parser, std::istream& is) {
using ResultType = typename Parser::ResultType;
ResultType result{};

parser.Reset();
SubscriberSink<ResultType> sink(result);
parser.Subscribe(sink);

ParserState state;
state.PushParser(parser);
state.ProcessInput(is);

return result;
}

} // namespace impl

template <typename T, typename Parser>
Expand Down
25 changes: 23 additions & 2 deletions universal/include/userver/formats/json/serialize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <fmt/format.h>

#include <userver/formats/json/parser/parser.hpp>
#include <userver/formats/json/value.hpp>
#include <userver/utils/fast_pimpl.hpp>
#include <userver/utils/fmt_compat.hpp>
Expand All @@ -30,6 +31,26 @@ formats::json::Value FromString(std::string_view doc);
/// Parse JSON from stream
formats::json::Value FromStream(std::istream& is);

/// Parse T from JSON string
template <typename T>
T FromStringAs(std::string_view doc) {
if (doc.empty()) {
throw ParseException("JSON document is empty");
}
auto parser = parser::CreateParser<T>();
return parser::impl::ParseSingle(*parser, doc);
};

/// Parse T from JSON stream
template <typename T>
T FromStreamAs(std::istream& is) {
if (!is) {
throw BadStreamException(is);
}
auto parser = parser::CreateParser<T>();
return parser::impl::ParseSingle(*parser, is);
};

/// Serialize JSON to stream
void Serialize(const formats::json::Value& doc, std::ostream& os);

Expand Down Expand Up @@ -89,8 +110,8 @@ struct fmt::formatter<USERVER_NAMESPACE::formats::json::Value> : fmt::formatter<
constexpr static auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); }

template <typename FormatContext>
auto format(const USERVER_NAMESPACE::formats::json::Value& value, FormatContext& ctx)
USERVER_FMT_CONST->decltype(ctx.out()) {
auto format(const USERVER_NAMESPACE::formats::json::Value& value, FormatContext& ctx) USERVER_FMT_CONST
-> decltype(ctx.out()) {
const USERVER_NAMESPACE::formats::json::impl::StringBuffer buffer(value);
return fmt::format_to(ctx.out(), "{}", buffer.GetStringView());
}
Expand Down
Loading