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