userver: userver/formats/json/value.hpp Source File
Loading...
Searching...
No Matches
value.hpp
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;
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
58///
59/// To iterate over `Value` as object use formats::common::Items.
60class Value {
61public:
62 struct IterTraits {
63 using ValueType = formats::json::Value;
64 using Reference = const formats::json::Value&;
65 using Pointer = const formats::json::Value*;
66 using ContainerType = Value;
67 };
69
70 using const_iterator = Iterator<IterTraits, common::IteratorDirection::kForward>;
71 using const_reverse_iterator = Iterator<IterTraits, common::IteratorDirection::kReverse>;
72 using Exception = formats::json::Exception;
73 using ParseException = formats::json::ParseException;
74 using ExceptionWithPath = formats::json::ExceptionWithPath;
75 using Builder = ValueBuilder;
76
77 /// @brief Constructs a Value that holds a null.
79
80 Value(const Value&) = default;
81 Value(Value&&) noexcept;
82
83 Value& operator=(const Value&) & = default;
84 Value& operator=(Value&&) noexcept;
85
86 template <class T>
87 Value& operator=(T&&) && {
88 static_assert(
89 !sizeof(T),
90 "You're assigning to a temporary formats::json::Value! Use "
91 "formats::json::ValueBuilder for data modifications."
92 );
93 return *this;
94 }
95
96 /// @brief Access member by key for read.
97 /// @throw TypeMismatchException if not a missing value, an object or null.
98 Value operator[](std::string_view key) const;
99 /// @brief Access array member by index for read.
100 /// @throw TypeMismatchException if not an array value.
101 /// @throw OutOfBoundsException if index is greater or equal
102 /// than size.
103 Value operator[](std::size_t index) const;
104
105 /// @brief Returns an iterator to the beginning of the held array or map.
106 /// @throw TypeMismatchException if not an array, object, or null.
107 ///
108 /// To iterate over `Value` as object use formats::common::Items.
109 const_iterator begin() const;
110
111 /// @brief Returns an iterator to the end of the held array or map.
112 /// @throw TypeMismatchException if not an array, object, or null.
113 const_iterator end() const;
114
115 /// @brief Returns an iterator to the reversed begin of the held array.
116 /// @throw TypeMismatchException if not an array or null.
117 const_reverse_iterator rbegin() const;
118
119 /// @brief Returns an iterator to the reversed end of the held array.
120 /// @throw TypeMismatchException if not an array or null.
121 const_reverse_iterator rend() const;
122
123 /// @brief Returns whether the array or object is empty.
124 /// Returns true for null.
125 /// @throw TypeMismatchException if not an array, object, or null.
126 bool IsEmpty() const;
127
128 /// @brief Returns array size, object members count, or 0 for null.
129 /// @throw TypeMismatchException if not an array, object, or null.
130 std::size_t GetSize() const;
131
132 /// @brief Compares values.
133 /// @throw MemberMissingException if `*this` or `other` is missing.
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 /// @brief Returns value of *this converted to the result type of
174 /// Parse(const Value&, parse::To<T>). Almost always it is T.
175 /// @throw Anything derived from std::exception.
176 ///
177 /// ## Example usage:
178 ///
179 /// @snippet formats/json/value_test.cpp Sample formats::json::Value::As<T>() usage
180 ///
181 /// @see @ref scripts/docs/en/userver/formats.md
182 template <typename T>
183 auto As() const;
184
185 /// @brief Returns value of *this converted to T or T(args) if
186 /// this->IsMissing().
187 /// @throw Anything derived from std::exception.
188 template <typename T, typename First, typename... Rest>
189 auto As(First&& default_arg, Rest&&... more_default_args) const;
190
191 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
192 /// @throw Anything derived from std::exception.
193 /// @note Use as `value.As<T>({})`
194 template <typename T>
196
197 /// @brief Extracts the specified type with relaxed type checks.
198 /// For example, `true` may be converted to 1.0.
199 template <typename T>
200 T ConvertTo() const;
201
202 /// Extracts the specified type with strict type checks, or constructs the
203 /// default value when the field is not present
204 template <typename T, typename First, typename... Rest>
205 T ConvertTo(First&& default_arg, Rest&&... more_default_args) const;
206
207 /// @brief Returns true if *this holds a `key`.
208 /// @throw TypeMismatchException if `*this` is not a map or null.
209 bool HasMember(std::string_view key) const;
210
211 /// @brief Returns full path to this value.
212 std::string GetPath() const;
213
214 /// @cond
215 void DropRootPath();
216 /// @endcond
217
218 /// @brief Returns new value that is an exact copy of the existing one
219 /// but references different memory (a deep copy of a *this). The returned
220 /// value is a root value with path '/'.
221 /// @throws MemberMissingException if `this->IsMissing()`.
222 Value Clone() const;
223
224 /// @throw MemberMissingException if `this->IsMissing()`.
225 void CheckNotMissing() const;
226
227 /// @throw TypeMismatchException if `*this` is not an array or null.
228 void CheckArrayOrNull() const;
229
230 /// @throw TypeMismatchException if `*this` is not a map or null.
231 void CheckObjectOrNull() const;
232
233 /// @throw TypeMismatchException if `*this` is not an array.
234 void CheckArray() const;
235
236 /// @throw TypeMismatchException if `*this` is not a map.
237 void CheckObject() const;
238
239 /// @throw TypeMismatchException if `*this` is not a map, array or null.
241
242 /// @throw TypeMismatchException if `*this` is not an array or null;
243 /// `OutOfBoundsException` if `index >= this->GetSize()`.
244 void CheckInBounds(std::size_t index) const;
245
246 /// @brief Returns true if *this is a first (root) value.
247 bool IsRoot() const noexcept;
248
249 /// @brief Returns true if `*this` and `other` reference the value by the same
250 /// pointer.
251 bool DebugIsReferencingSameMemory(const Value& other) const { return value_ptr_ == other.value_ptr_; }
252
253private:
254 struct EmplaceEnabler {
255 explicit EmplaceEnabler() = default;
256 };
257
258 class LazyDetachedPath;
259
260public:
261 /// @cond
262 Value(
263 EmplaceEnabler,
264 const impl::VersionedValuePtr& root,
265 const impl::Value* root_ptr_for_path,
266 const impl::Value* value_ptr,
267 int depth
268 );
269
270 Value(
271 EmplaceEnabler,
272 const impl::VersionedValuePtr& root,
273 impl::Value* root_ptr_for_path,
274 LazyDetachedPath&& lazy_detached_path
275 );
276 /// @endcond
277
278private:
279 explicit Value(impl::VersionedValuePtr root) noexcept;
280
281 bool IsUniqueReference() const;
282 void EnsureNotMissing() const;
283 const impl::Value& GetNative() const;
284 impl::Value& GetNative();
285 void SetNative(impl::Value&); // does not copy
286 int GetExtendedType() const;
287
288 impl::VersionedValuePtr holder_{};
289 impl::Value* root_ptr_for_path_{nullptr};
290 impl::Value* value_ptr_{nullptr};
291 /// Depth of the node to ease recursive traversal in GetPath()
292 int depth_{0};
293
294 // We don't want to calculate the path for missing node before it is
295 // explicitly requested, because GetPath() call is very costly.
296 // This helps with patterns like 'json["missing"].As<T>({})':
297 // path is not needed here (note default arg), and if we have a lot of missing
298 // keys during parsing we save a lot of expensive calculations.
299 class LazyDetachedPath final {
300 public:
301 LazyDetachedPath() noexcept;
302 LazyDetachedPath(impl::Value* parent_value_ptr, int parent_depth, std::string_view key);
303
304 LazyDetachedPath(const LazyDetachedPath&);
305 LazyDetachedPath(LazyDetachedPath&&) noexcept;
306 LazyDetachedPath& operator=(const LazyDetachedPath&);
307 LazyDetachedPath& operator=(LazyDetachedPath&&) noexcept;
308
309 std::string Get(const impl::Value* root) const;
310 LazyDetachedPath Chain(std::string_view key) const;
311
312 private:
313 impl::Value* parent_value_ptr_{nullptr};
314 int parent_depth_{0};
315 std::string virtual_path_{};
316 };
317
318 LazyDetachedPath lazy_detached_path_;
319
320 template <typename, common::IteratorDirection>
321 friend class Iterator;
322 friend class ValueBuilder;
323 friend class StringBuilder;
324 friend class Schema;
325 friend class impl::InlineObjectBuilder;
326 friend class impl::InlineArrayBuilder;
327 friend class impl::MutableValueWrapper;
328 friend class parser::JsonValueParser;
329 friend class impl::StringBuffer;
330
331 friend bool Parse(const Value& value, parse::To<bool>);
332 friend std::int64_t Parse(const Value& value, parse::To<std::int64_t>);
333 friend std::uint64_t Parse(const Value& value, parse::To<std::uint64_t>);
334 friend double Parse(const Value& value, parse::To<double>);
335 friend std::string Parse(const Value& value, parse::To<std::string>);
336
337 friend formats::json::Value FromString(std::string_view);
338 friend formats::json::Value FromStream(std::istream&);
339 friend void Serialize(const formats::json::Value&, std::ostream&);
340 friend std::string ToString(const formats::json::Value&);
341 friend std::string ToStableString(const formats::json::Value&);
342 friend std::string ToStableString(formats::json::Value&&);
343 friend std::string ToPrettyString(const formats::json::Value& doc, PrettyFormat format);
344 friend logging::LogHelper& operator<<(logging::LogHelper&, const Value&);
345};
346
347template <typename T>
348USERVER_IMPL_NODEBUG auto Value::As() const {
349 static_assert(
350 formats::common::impl::HasParse<Value, T>,
351 "There is no `Parse(const Value&, formats::parse::To<T>)` in namespace of `T` or `formats::parse`. "
352 "Probably you forgot to include the <userver/formats/parse/common_containers.hpp> or you "
353 "have not provided a `Parse` function overload."
354 );
355
356 return Parse(*this, formats::parse::To<T>{});
357}
358
359bool Parse(const Value& value, parse::To<bool>);
360
361std::int64_t Parse(const Value& value, parse::To<std::int64_t>);
362
363std::uint64_t Parse(const Value& value, parse::To<std::uint64_t>);
364
365double Parse(const Value& value, parse::To<double>);
366
367std::string Parse(const Value& value, parse::To<std::string>);
368
369template <>
370bool Value::ConvertTo<bool>() const;
371
372template <>
373int64_t Value::ConvertTo<int64_t>() const;
374
375template <>
376uint64_t Value::ConvertTo<uint64_t>() const;
377
378template <>
379double Value::ConvertTo<double>() const;
380
381template <>
382std::string Value::ConvertTo<std::string>() const;
383
384template <typename T, typename First, typename... Rest>
385auto Value::As(First&& default_arg, Rest&&... more_default_args) const {
386 if (IsMissing() || IsNull()) {
387 // intended raw ctor call, sometimes casts
388 // NOLINTNEXTLINE(google-readability-casting)
389 return decltype(As<T>())(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
390 }
391 return As<T>();
392}
393
394template <typename T>
396 return (IsMissing() || IsNull()) ? decltype(As<T>())() : As<T>();
397}
398
399template <typename T>
400USERVER_IMPL_NODEBUG T Value::ConvertTo() const {
401 if constexpr (formats::common::impl::HasConvert<Value, T>) {
402 return Convert(*this, formats::parse::To<T>{});
403 } else if constexpr (formats::common::impl::HasParse<Value, T>) {
404 return Parse(*this, formats::parse::To<T>{});
405 } else {
406 static_assert(
407 !sizeof(T),
408 "There is no `Convert(const Value&, formats::parse::To<T>)` or `Parse(const Value&, formats::parse::To<T>)`"
409 "in namespace of `T` or `formats::parse`. Probably you have not provided a `Convert` function overload."
410 );
411 }
412}
413
414template <typename T, typename First, typename... Rest>
415T Value::ConvertTo(First&& default_arg, Rest&&... more_default_args) const {
416 if (IsMissing() || IsNull()) {
417 // NOLINTNEXTLINE(google-readability-casting)
418 return T(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
419 }
420 return ConvertTo<T>();
421}
422
423inline Value Parse(const Value& value, parse::To<Value>) { return value; }
424
425inline Value Parse(Value&& value, parse::To<Value>) { return std::move(value); }
426
427std::chrono::microseconds Parse(const Value& value, parse::To<std::chrono::microseconds>);
428
429std::chrono::milliseconds Parse(const Value& value, parse::To<std::chrono::milliseconds>);
430
431std::chrono::minutes Parse(const Value& value, parse::To<std::chrono::minutes>);
432
433std::chrono::hours Parse(const Value& value, parse::To<std::chrono::hours>);
434
435/// @brief Wrapper for handy python-like iteration over a map
436///
437/// @code
438/// for (const auto& [name, value]: Items(map)) ...
439/// @endcode
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