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