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