userver: userver/formats/json/parser/typed_parser.hpp Source File
Loading...
Searching...
No Matches
typed_parser.hpp
1#pragma once
2
3#include <optional>
4
5#include <userver/formats/json/parser/base_parser.hpp>
6#include <userver/utils/assert.hpp>
7
8USERVER_NAMESPACE_BEGIN
9
10namespace formats::json::parser {
11
12template <typename T>
14public:
15 virtual ~Subscriber() = default;
16
17 virtual void OnSend(T&&) = 0;
18};
19
20template <typename T>
21class SubscriberSink final : public Subscriber<T> {
22public:
23 SubscriberSink(T& data)
24 : data_(data)
25 {}
26
27 void OnSend(T&& value) override { data_ = std::move(value); }
28
29private:
30 T& data_;
31};
32
33template <typename T>
34class SubscriberSinkOptional final : public Subscriber<T>, public Subscriber<std::optional<T>> {
35public:
36 SubscriberSinkOptional(std::optional<T>& data)
37 : data_(data)
38 {}
39
40 void OnSend(T&& value) override { data_ = std::move(value); }
41
42 void OnSend(std::optional<T>&& value) override { data_ = std::move(value); }
43
44private:
45 std::optional<T>& data_;
46};
47
48/// @brief Main base class for SAX parsers
49///
50/// There are two main groups of SAX parser classes:
51/// - typed parser
52/// - proxy parser
53///
54/// TypedParser derivative is a parser that handles JSON tokens by itself.
55/// It implements methods of BaseParser for handling specific tokens (e.g.
56/// Null(), StartArray(), Double()). Usually parser implements only 1-2 methods
57/// of BaseParser for handling a fixed set of JSON tokens and leaves default
58/// implementation for the rest (iow, treat other JSON tokens as a parse error).
59///
60/// Parser usually maintains its state as a field(s). Parse methods are called
61/// when a specific input token is read, and implementation updates the parser
62/// state. When finished, the parser calls SetResult() with the cooked value. It
63/// pops current parser from the parser stack and signals the subscriber with
64/// the result.
65///
66/// TypedParser may delegate part of its job to subparsers. It is very common to
67/// define a parser for an object/array and reuse field parsers for JSON object
68/// fields parsing. A subparser is usually implemented as a field of a parser
69/// class. When such parser wants to start subobject parsing, it pushes the
70/// subparser onto the stack (and maybe calls some of its parse methods).
71///
72/// E.g.:
73///
74/// ~~~~~~~~~~~~~~{.cpp}
75/// void SomeParser::Double(double d) {
76/// subparser_->Reset();
77/// parser_state_->PushParser(subparser_.GetParser());
78/// subparser_->Double(d);
79/// }
80/// ~~~~~~~~~~~~~~
81///
82/// You may also implement a proxy parser. It derives neither from TypedParser
83/// nor any other userver's parser class. A proxy parser is a class that
84/// delegates the whole job of input token handling to subparser(s), but somehow
85/// mutates the result (e.g. converts or validates it) - proxies the result. It
86/// doesn't implement any JSON token handling methods by itself. Usually proxy
87/// parser stores a subparser as a field and maybe stores some housekeeping
88/// settings for result handling.
89///
90/// Duck typing is used in proxy parsers to negate virtual methods overhead.
91/// A proxy parser must implement the following methods:
92///
93/// ~~~~~~~~~{.cpp}
94/// class ProxyParser final
95/// : public formats::json::parser::Subscriber<Subparser::ResultType> {
96/// public:
97/// // Define result type that will be passed to OnSend() of a subscriber
98/// using ResultType = Result;
99///
100/// ProxyParser() {
101/// // Proxy parser wants to be called when the subparser
102/// // signals with the result
103/// subparser.Subscribe(*this);
104/// }
105///
106/// // Reset() of proxy parser MUST call Reset() of subparsers in contrast to
107/// // a typed parser as a proxy parser doesn't control pushing of subparser
108/// // onto the stack.
109/// void Reset() {
110/// subparser_.Reset();
111/// }
112///
113/// void Subscribe(formats::json::parser::Subscriber<Result>& subscriber) {
114/// subscriber_ = &subscriber;
115/// }
116///
117/// // The core method of proxy parser. It converts/filters the result value
118/// // and signals the (maybe mutated) result further to its subscriber.
119/// void OnSend(Subparser::ResultType&& result) override {
120/// if (subscriber_) subscriber_->OnSend(Result(std::move(result)));
121/// }
122///
123/// // Returns a typed parser that is responsible for actual JSON parsing.
124/// auto& GetParser() { return subparser_.GetParser(); }
125///
126/// private:
127/// Subparser subparser_;
128/// Subscriber* subscriber_{nullptr};
129/// }
130/// ~~~~~~~~~
131///
132template <typename T>
133class TypedParser : public BaseParser {
134public:
135 void Subscribe(Subscriber<T>& subscriber) { subscriber_ = &subscriber; }
136
137 using ResultType = T;
138
139 /// Resets parser's internal state.
140 /// It should not call Reset() of subparsers (if any).
141 /// Subparsers' Reset() should be called just before pushing it onto the
142 /// stack.
143 virtual void Reset() {}
144
145 /// Returns an actual parser.
146 /// It is commonly used in PushParser() to identify typed parser
147 /// of a proxy parser.
148 TypedParser<T>& GetParser() { return *this; }
149
150protected:
151 void SetResult(T&& value) {
152 parser_state_->PopMe(*this);
153 if (subscriber_) {
154 subscriber_->OnSend(std::move(value));
155 }
156 }
157
158private:
159 Subscriber<T>* subscriber_{nullptr};
160};
161
162namespace impl {
163
164template <typename Parser>
165typename Parser::ResultType ParseSingle(Parser& parser, std::string_view input) {
166 using ResultType = typename Parser::ResultType;
167 ResultType result{};
168
169 parser.Reset();
170 SubscriberSink<ResultType> sink(result);
171 parser.Subscribe(sink);
172
173 ParserState state;
174 state.PushParser(parser);
175 state.ProcessInput(input);
176
177 return result;
178}
179
180} // namespace impl
181
182template <typename T, typename Parser>
183T ParseToType(std::string_view input) {
184 Parser parser;
185 return impl::ParseSingle(parser, input);
186}
187
188} // namespace formats::json::parser
189
190USERVER_NAMESPACE_END