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