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