userver: userver/formats/json/parser/typed_parser.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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