userver: userver/formats/json/value.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
value.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/json/value.hpp
4/// @brief @copybrief formats::json::Value
5
6#include <string_view>
7#include <type_traits>
8
9#include <userver/formats/common/items.hpp>
10#include <userver/formats/common/meta.hpp>
11#include <userver/formats/json/exception.hpp>
12#include <userver/formats/json/impl/types.hpp>
13#include <userver/formats/json/iterator.hpp>
14#include <userver/formats/json/serialize.hpp>
15#include <userver/formats/json/string_builder_fwd.hpp>
16#include <userver/formats/parse/common.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace logging {
21class LogHelper;
22} // namespace logging
23
24namespace formats::json {
25namespace impl {
26class InlineObjectBuilder;
27class InlineArrayBuilder;
28class MutableValueWrapper;
29class StringBuffer;
30
31// do not make a copy of string
32impl::Value MakeJsonStringViewValue(std::string_view view);
33
34} // namespace impl
35
36class ValueBuilder;
37
38namespace parser {
39class JsonValueParser;
40} // namespace parser
41
42/// @ingroup userver_universal userver_containers userver_formats
43///
44/// @brief Non-mutable JSON value representation.
45///
46/// Class provides non mutable access JSON value. For modification and
47/// construction of new JSON values use formats::json::ValueBuilder.
48///
49/// ## Example usage:
50///
51/// @snippet formats/json/value_test.cpp Sample formats::json::Value usage
52///
53/// @see @ref scripts/docs/en/userver/formats.md
54class Value final {
55 public:
56 struct IterTraits {
57 using ValueType = formats::json::Value;
58 using Reference = const formats::json::Value&;
59 using Pointer = const formats::json::Value*;
60 using ContainerType = Value;
61 };
63
64 using const_iterator =
70 using Builder = ValueBuilder;
71
72 /// @brief Constructs a Value that holds a null.
73 Value() noexcept = default;
74
75 Value(const Value&) = default;
76 Value(Value&&) noexcept = default;
77
78 Value& operator=(const Value&) & = default;
79 Value& operator=(Value&& other) & noexcept = default;
80
81 template <class T>
82 Value& operator=(T&&) && {
83 static_assert(!sizeof(T),
84 "You're assigning to a temporary formats::json::Value! Use "
85 "formats::json::ValueBuilder for data modifications.");
86 return *this;
87 }
88
89 /// @brief Access member by key for read.
90 /// @throw TypeMismatchException if not a missing value, an object or null.
92 /// @brief Access array member by index for read.
93 /// @throw TypeMismatchException if not an array value.
94 /// @throw OutOfBoundsException if index is greater or equal
95 /// than size.
97
98 /// @brief Returns an iterator to the beginning of the held array or map.
99 /// @throw TypeMismatchException if not an array, object, or null.
101
102 /// @brief Returns an iterator to the end of the held array or map.
103 /// @throw TypeMismatchException if not an array, object, or null.
105
106 /// @brief Returns an iterator to the reversed begin of the held array.
107 /// @throw TypeMismatchException if not an array or null.
109
110 /// @brief Returns an iterator to the reversed end of the held array.
111 /// @throw TypeMismatchException if not an array or null.
113
114 /// @brief Returns whether the array or object is empty.
115 /// Returns true for null.
116 /// @throw TypeMismatchException if not an array, object, or null.
117 bool IsEmpty() const;
118
119 /// @brief Returns array size, object members count, or 0 for null.
120 /// @throw TypeMismatchException if not an array, object, or null.
122
123 /// @brief Compares values.
124 /// @throw MemberMissingException if `*this` or `other` is missing.
125 bool operator==(const Value& other) const;
126 bool operator!=(const Value& other) const;
127
128 /// @brief Returns true if *this holds nothing. When `IsMissing()` returns
129 /// `true` any attempt to get the actual value or iterate over *this will
130 bool IsMissing() const noexcept;
131
132 /// @brief Returns true if *this holds a null (Type::kNull).
133 bool IsNull() const noexcept;
134
135 /// @brief Returns true if *this holds a bool.
136 bool IsBool() const noexcept;
137
138 /// @brief Returns true if *this holds an int.
139 bool IsInt() const noexcept;
140
141 /// @brief Returns true if *this holds an int64_t.
142 bool IsInt64() const noexcept;
143
144 /// @brief Returns true if *this holds an uint64_t.
145 bool IsUInt64() const noexcept;
146
147 /// @brief Returns true if *this holds a double.
148 bool IsDouble() const noexcept;
149
150 /// @brief Returns true if *this is holds a std::string.
151 bool IsString() const noexcept;
152
153 /// @brief Returns true if *this is holds an array (Type::kArray).
154 bool IsArray() const noexcept;
155
156 /// @brief Returns true if *this holds a map (Type::kObject).
157 bool IsObject() const noexcept;
158
159 // clang-format off
160
161 /// @brief Returns value of *this converted to T.
162 /// @throw Anything derived from std::exception.
163 ///
164 /// ## Example usage:
165 ///
166 /// @snippet formats/json/value_test.cpp Sample formats::json::Value::As<T>() usage
167 ///
168 /// @see @ref scripts/docs/en/userver/formats.md
169
170 // clang-format on
171
172 template <typename T>
173 T As() const;
174
175 /// @brief Returns value of *this converted to T or T(args) if
176 /// this->IsMissing().
177 /// @throw Anything derived from std::exception.
178 template <typename T, typename First, typename... Rest>
180
181 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
182 /// @throw Anything derived from std::exception.
183 /// @note Use as `value.As<T>({})`
184 template <typename T>
185 T As(DefaultConstructed) const;
186
187 /// @brief Extracts the specified type with relaxed type checks.
188 /// For example, `true` may be converted to 1.0.
189 template <typename T>
190 T ConvertTo() const;
191
192 /// Extracts the specified type with strict type checks, or constructs the
193 /// default value when the field is not present
194 template <typename T, typename First, typename... Rest>
196
197 /// @brief Returns true if *this holds a `key`.
198 /// @throw TypeMismatchException if `*this` is not a map or null.
200
201 /// @brief Returns full path to this value.
203
204 /// @brief Returns new value that is an exact copy if the existing one
205 /// but references different memory (a deep copy of a *this). The returned
206 /// value is a root value with path '/'.
207 /// @throws MemberMissingException id `this->IsMissing()`.
208 Value Clone() const;
209
210 /// @throw MemberMissingException if `this->IsMissing()`.
211 void CheckNotMissing() const;
212
213 /// @throw MemberMissingException if `*this` is not an array or null.
214 void CheckArrayOrNull() const;
215
216 /// @throw TypeMismatchException if `*this` is not a map or null.
217 void CheckObjectOrNull() const;
218
219 /// @throw TypeMismatchException if `*this` is not a map.
220 void CheckObject() const;
221
222 /// @throw TypeMismatchException if `*this` is not a map, array or null.
224
225 /// @throw TypeMismatchException if `*this` is not a map, array or null;
226 /// `OutOfBoundsException` if `index >= this->GetSize()`.
228
229 /// @brief Returns true if *this is a first (root) value.
230 bool IsRoot() const noexcept;
231
232 /// @brief Returns true if `*this` and `other` reference the value by the same
233 /// pointer.
235 return value_ptr_ == other.value_ptr_;
236 }
237
238 private:
239 class EmplaceEnabler {};
240 class LazyDetachedPath;
241
242 public:
243 /// @cond
245 const impl::Value& value, int depth);
246 /// @endcond
247
248 private:
249 explicit Value(impl::VersionedValuePtr root) noexcept;
252
253 bool IsUniqueReference() const;
254 void EnsureNotMissing();
255 const impl::Value& GetNative() const;
256 impl::Value& GetNative();
257 void SetNative(impl::Value&); // does not copy
258 int GetExtendedType() const;
259
261 impl::Value* value_ptr_{nullptr};
262 /// Depth of the node to ease recursive traversal in GetPath()
263 int depth_{0};
264
265 // We don't want to calculate the path for missing node before it is
266 // explicitly requested, because GetPath() call is very costly.
267 // This helps with patterns like 'json["missing"].As<T>({})':
268 // path is not needed here (note default arg), and if we have a lot of missing
269 // keys during parsing we save a lot of expensive calculations.
270 class LazyDetachedPath final {
271 public:
272 LazyDetachedPath() noexcept;
275
280
281 std::string Get(const impl::Value* root) const;
283
284 private:
285 impl::Value* parent_value_ptr_{nullptr};
286 int parent_depth_{0};
288 };
289
291
292 template <typename, common::IteratorDirection>
293 friend class Iterator;
294 friend class ValueBuilder;
295 friend class StringBuilder;
296 friend class impl::InlineObjectBuilder;
297 friend class impl::InlineArrayBuilder;
298 friend class impl::MutableValueWrapper;
299 friend class parser::JsonValueParser;
300 friend class impl::StringBuffer;
301
304 friend void Serialize(const formats::json::Value&, std::ostream&);
305 friend std::string ToString(const formats::json::Value&);
309};
310
311template <typename T>
312T Value::As() const {
313 static_assert(formats::common::impl::kHasParse<Value, T>,
314 "There is no `Parse(const Value&, formats::parse::To<T>)` "
315 "in namespace of `T` or `formats::parse`. "
316 "Probably you forgot to include the "
317 "<userver/formats/parse/common_containers.hpp> or you "
318 "have not provided a `Parse` function overload.");
319
320 return Parse(*this, formats::parse::To<T>{});
321}
322
323template <>
324bool Value::As<bool>() const;
325
326template <>
327int64_t Value::As<int64_t>() const;
328
329template <>
330uint64_t Value::As<uint64_t>() const;
331
332template <>
333double Value::As<double>() const;
334
335template <>
336std::string Value::As<std::string>() const;
337
338template <>
339bool Value::ConvertTo<bool>() const;
340
341template <>
342int64_t Value::ConvertTo<int64_t>() const;
343
344template <>
345uint64_t Value::ConvertTo<uint64_t>() const;
346
347template <>
348double Value::ConvertTo<double>() const;
349
350template <>
351std::string Value::ConvertTo<std::string>() const;
352
353template <typename T, typename First, typename... Rest>
354T Value::As(First&& default_arg, Rest&&... more_default_args) const {
355 if (IsMissing() || IsNull()) {
356 // intended raw ctor call, sometimes casts
357 // NOLINTNEXTLINE(google-readability-casting)
358 return T(std::forward<First>(default_arg),
359 std::forward<Rest>(more_default_args)...);
360 }
361 return As<T>();
362}
363
364template <typename T>
365T Value::As(Value::DefaultConstructed) const {
366 return (IsMissing() || IsNull()) ? T() : As<T>();
367}
368
369template <typename T>
370T Value::ConvertTo() const {
371 if constexpr (formats::common::impl::kHasConvert<Value, T>) {
372 return Convert(*this, formats::parse::To<T>{});
373 } else if constexpr (formats::common::impl::kHasParse<Value, T>) {
374 return Parse(*this, formats::parse::To<T>{});
375 } else {
376 static_assert(
377 !sizeof(T),
378 "There is no `Convert(const Value&, formats::parse::To<T>)` or"
379 "`Parse(const Value&, formats::parse::To<T>)`"
380 "in namespace of `T` or `formats::parse`. "
381 "Probably you have not provided a `Convert` function overload.");
382 }
383}
384
385template <typename T, typename First, typename... Rest>
386T Value::ConvertTo(First&& default_arg, Rest&&... more_default_args) const {
387 if (IsMissing() || IsNull()) {
388 // NOLINTNEXTLINE(google-readability-casting)
389 return T(std::forward<First>(default_arg),
390 std::forward<Rest>(more_default_args)...);
391 }
392 return ConvertTo<T>();
393}
394
395inline Value Parse(const Value& value, parse::To<Value>) { return value; }
396
397/// @brief Wrapper for handy python-like iteration over a map
398///
399/// @code
400/// for (const auto& [name, value]: Items(map)) ...
401/// @endcode
402using formats::common::Items;
403
404} // namespace formats::json
405
406/// Although we provide user defined literals, please beware that
407/// 'using namespace ABC' may contradict code style of your company.
408namespace formats::literals {
409
410json::Value operator"" _json(const char* str, std::size_t len);
411
412} // namespace formats::literals
413
414USERVER_NAMESPACE_END