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