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 <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