Github   Telegram
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 <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} // namespace impl
31
32class ValueBuilder;
33
34namespace parser {
35class JsonValueParser;
36} // namespace parser
37
38/// @ingroup userver_containers userver_formats
39///
40/// @brief Non-mutable JSON value representation.
41///
42/// Class provides non mutable access JSON value. For modification and
43/// construction of new JSON values use formats::json::ValueBuilder.
44///
45/// ## Example usage:
46///
47/// @snippet formats/json/value_test.cpp Sample formats::json::Value usage
48///
49/// @see @ref md_en_userver_formats
50class Value final {
51 public:
52 struct IterTraits {
53 using ValueType = formats::json::Value;
54 using Reference = const formats::json::Value&;
55 using Pointer = const formats::json::Value*;
56 using ContainerType = Value;
57 };
59
60 using const_iterator =
66 using Builder = ValueBuilder;
67
68 /// @brief Constructs a Value that holds a null.
69 Value() noexcept = default;
70
71 Value(const Value&) = default;
72 Value(Value&&) noexcept = default;
73
74 Value& operator=(const Value&) & = default;
75 Value& operator=(Value&& other) & noexcept = default;
76
77 template <class T>
78 Value& operator=(T&&) && {
79 static_assert(!sizeof(T),
80 "You're assigning to a temporary formats::json::Value! Use "
81 "formats::json::ValueBuilder for data modifications.");
82 return *this;
83 }
84
85 /// @brief Access member by key for read.
86 /// @throw TypeMismatchException if not a missing value, an object or null.
88 /// @brief Access array member by index for read.
89 /// @throw TypeMismatchException if not an array value.
90 /// @throw OutOfBoundsException if index is greater or equal
91 /// than size.
93
94 /// @brief Returns an iterator to the beginning of the held array or map.
95 /// @throw TypeMismatchException if not an array, object, or null.
97
98 /// @brief Returns an iterator to the end of the held array or map.
99 /// @throw TypeMismatchException if not an array, object, or null.
101
102 /// @brief Returns an iterator to the reversed begin of the held array.
103 /// @throw TypeMismatchException if not an array or null.
105
106 /// @brief Returns an iterator to the reversed end of the held array.
107 /// @throw TypeMismatchException if not an array or null.
109
110 /// @brief Returns whether the array or object is empty.
111 /// Returns true for null.
112 /// @throw TypeMismatchException if not an array, object, or null.
113 bool IsEmpty() const;
114
115 /// @brief Returns array size, object members count, or 0 for null.
116 /// @throw TypeMismatchException if not an array, object, or null.
118
119 /// @brief Compares values.
120 /// @throw MemberMissingException if `*this` or `other` is missing.
121 bool operator==(const Value& other) const;
122 bool operator!=(const Value& other) const;
123
124 /// @brief Returns true if *this holds nothing. When `IsMissing()` returns
125 /// `true` any attempt to get the actual value or iterate over *this will
126 bool IsMissing() const noexcept;
127
128 /// @brief Returns true if *this holds a null (Type::kNull).
129 bool IsNull() const noexcept;
130
131 /// @brief Returns true if *this holds a bool.
132 bool IsBool() const noexcept;
133
134 /// @brief Returns true if *this holds an int.
135 bool IsInt() const noexcept;
136
137 /// @brief Returns true if *this holds an int64_t.
138 bool IsInt64() const noexcept;
139
140 /// @brief Returns true if *this holds an uint64_t.
141 bool IsUInt64() const noexcept;
142
143 /// @brief Returns true if *this holds a double.
144 bool IsDouble() const noexcept;
145
146 /// @brief Returns true if *this is holds a std::string.
147 bool IsString() const noexcept;
148
149 /// @brief Returns true if *this is holds an array (Type::kArray).
150 bool IsArray() const noexcept;
151
152 /// @brief Returns true if *this holds a map (Type::kObject).
153 bool IsObject() const noexcept;
154
155 // clang-format off
156
157 /// @brief Returns value of *this converted to T.
158 /// @throw Anything derived from std::exception.
159 ///
160 /// ## Example usage:
161 ///
162 /// @snippet formats/json/value_test.cpp Sample formats::json::Value::As<T>() usage
163 ///
164 /// @see @ref md_en_userver_formats
165
166 // clang-format on
167
168 template <typename T>
169 T As() const;
170
171 /// @brief Returns value of *this converted to T or T(args) if
172 /// this->IsMissing().
173 /// @throw Anything derived from std::exception.
174 template <typename T, typename First, typename... Rest>
176
177 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
178 /// @throw Anything derived from std::exception.
179 /// @note Use as `value.As<T>({})`
180 template <typename T>
181 T As(DefaultConstructed) const;
182
183 /// @brief Extracts the specified type with relaxed type checks.
184 /// For example, `true` may be converted to 1.0.
185 template <typename T>
186 T ConvertTo() const;
187
188 /// Extracts the specified type with strict type checks, or constructs the
189 /// default value when the field is not present
190 template <typename T, typename First, typename... Rest>
192
193 /// @brief Returns true if *this holds a `key`.
194 /// @throw TypeMismatchException if `*this` is not a map or null.
196
197 /// @brief Returns full path to this value.
199
200 /// @brief Returns new value that is an exact copy if the existing one
201 /// but references different memory (a deep copy of a *this). The returned
202 /// value is a root value with path '/'.
203 /// @throws MemberMissingException id `this->IsMissing()`.
204 Value Clone() const;
205
206 /// @throw MemberMissingException if `this->IsMissing()`.
207 void CheckNotMissing() const;
208
209 /// @throw MemberMissingException if `*this` is not an array or null.
210 void CheckArrayOrNull() const;
211
212 /// @throw TypeMismatchException if `*this` is not a map or null.
213 void CheckObjectOrNull() const;
214
215 /// @throw TypeMismatchException if `*this` is not a map.
216 void CheckObject() const;
217
218 /// @throw TypeMismatchException if `*this` is not a map, array or null.
220
221 /// @throw TypeMismatchException if `*this` is not a map, array or null;
222 /// `OutOfBoundsException` if `index >= this->GetSize()`.
224
225 /// @brief Returns true if *this is a first (root) value.
226 bool IsRoot() const noexcept;
227
228 /// @brief Returns true if `*this` and `other` reference the value by the same
229 /// pointer.
231 return value_ptr_ == other.value_ptr_;
232 }
233
234 private:
235 class EmplaceEnabler {};
236
237 public:
238 /// @cond
240 const impl::Value& value, int depth);
241 /// @endcond
242
243 private:
244 explicit Value(impl::VersionedValuePtr root) noexcept;
247
248 bool IsUniqueReference() const;
249 void EnsureNotMissing();
250 const impl::Value& GetNative() const;
251 impl::Value& GetNative();
252 void SetNative(impl::Value&); // does not copy
253 int GetExtendedType() const;
254
256 impl::Value* value_ptr_{nullptr};
257 /// Full path of node (only for missing nodes)
259 /// Depth of the node to ease recursive traversal in GetPath()
260 int depth_{0};
261
262 template <typename, common::IteratorDirection>
263 friend class Iterator;
264 friend class ValueBuilder;
265 friend class StringBuilder;
266 friend class impl::InlineObjectBuilder;
267 friend class impl::InlineArrayBuilder;
268 friend class impl::MutableValueWrapper;
269 friend class parser::JsonValueParser;
270 friend class impl::StringBuffer;
271
274 friend void Serialize(const formats::json::Value&, std::ostream&);
275 friend std::string ToString(const formats::json::Value&);
278};
279
280template <typename T>
281T Value::As() const {
282 static_assert(formats::common::impl::kHasParse<Value, T>,
283 "There is no `Parse(const Value&, formats::parse::To<T>)` "
284 "in namespace of `T` or `formats::parse`. "
285 "Probably you forgot to include the "
286 "<userver/formats/parse/common_containers.hpp> or you "
287 "have not provided a `Parse` function overload.");
288
289 return Parse(*this, formats::parse::To<T>{});
290}
291
292template <>
293bool Value::As<bool>() const;
294
295template <>
296int64_t Value::As<int64_t>() const;
297
298template <>
299uint64_t Value::As<uint64_t>() const;
300
301template <>
302double Value::As<double>() const;
303
304template <>
305std::string Value::As<std::string>() const;
306
307template <>
308bool Value::ConvertTo<bool>() const;
309
310template <>
311int64_t Value::ConvertTo<int64_t>() const;
312
313template <>
314uint64_t Value::ConvertTo<uint64_t>() const;
315
316template <>
317double Value::ConvertTo<double>() const;
318
319template <>
320std::string Value::ConvertTo<std::string>() const;
321
322template <typename T, typename First, typename... Rest>
323T Value::As(First&& default_arg, Rest&&... more_default_args) const {
324 if (IsMissing() || IsNull()) {
325 // intended raw ctor call, sometimes casts
326 // NOLINTNEXTLINE(google-readability-casting)
327 return T(std::forward<First>(default_arg),
328 std::forward<Rest>(more_default_args)...);
329 }
330 return As<T>();
331}
332
333template <typename T>
334T Value::As(Value::DefaultConstructed) const {
335 return (IsMissing() || IsNull()) ? T() : As<T>();
336}
337
338template <typename T>
339T Value::ConvertTo() const {
340 if constexpr (formats::common::impl::kHasConvert<Value, T>) {
341 return Convert(*this, formats::parse::To<T>{});
342 } else if constexpr (formats::common::impl::kHasParse<Value, T>) {
343 return Parse(*this, formats::parse::To<T>{});
344 } else {
345 static_assert(
346 !sizeof(T),
347 "There is no `Convert(const Value&, formats::parse::To<T>)` or"
348 "`Parse(const Value&, formats::parse::To<T>)`"
349 "in namespace of `T` or `formats::parse`. "
350 "Probably you have not provided a `Convert` function overload.");
351 }
352}
353
354template <typename T, typename First, typename... Rest>
355T Value::ConvertTo(First&& default_arg, Rest&&... more_default_args) const {
356 if (IsMissing() || IsNull()) {
357 // NOLINTNEXTLINE(google-readability-casting)
358 return T(std::forward<First>(default_arg),
359 std::forward<Rest>(more_default_args)...);
360 }
361 return ConvertTo<T>();
362}
363
364inline Value Parse(const Value& value, parse::To<Value>) { return value; }
365
366/// @brief Wrapper for handy python-like iteration over a map
367///
368/// @code
369/// for (const auto& [name, value]: Items(map)) ...
370/// @endcode
371using formats::common::Items;
372
373} // namespace formats::json
374
375USERVER_NAMESPACE_END