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