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