userver: userver/formats/json/value_builder.hpp Source File
Loading...
Searching...
No Matches
value_builder.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/json/value_builder.hpp
4/// @brief @copybrief formats::json::ValueBuilder
5
6#include <chrono>
7#include <string_view>
8#include <type_traits>
9
10#include <userver/compiler/impl/nodebug.hpp>
11#include <userver/formats/common/meta.hpp>
12#include <userver/formats/common/transfer_tag.hpp>
13#include <userver/formats/json/impl/mutable_value_wrapper.hpp>
14#include <userver/formats/json/value.hpp>
15#include <userver/utils/strong_typedef.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace formats::json {
20
21/// @ingroup userver_universal userver_containers userver_formats
22///
23/// @brief Builder for JSON.
24///
25/// Class provides methods for building JSON. For read only access to the
26/// existing JSON values use formats::json::Value.
27///
28/// ## Example usage:
29///
30/// @snippet universal/src/formats/json/value_builder_test.cpp Sample formats::json::ValueBuilder usage
31///
32/// ## Customization example:
33///
34/// @snippet universal/src/formats/json/value_builder_test.cpp Sample Customization formats::json::ValueBuilder usage
35///
36/// @see @ref scripts/docs/en/userver/formats.md
37class ValueBuilder final {
38public:
39 struct IterTraits {
40 using ValueType = formats::json::ValueBuilder;
41 using Reference = formats::json::ValueBuilder&;
42 using Pointer = formats::json::ValueBuilder*;
43 using ContainerType = impl::MutableValueWrapper;
44 };
45
46 using iterator = Iterator<IterTraits>;
47
48 /// Constructs a ValueBuilder that holds kNull
49 ValueBuilder() = default;
50
51 /// Constructs a valueBuilder that holds default value for provided `type`.
52 ValueBuilder(formats::common::Type type);
53
54 /// @brief Transfers the `ValueBuilder` object
55 /// @see formats::common::TransferTag for the transfer semantics
56 ValueBuilder(common::TransferTag, ValueBuilder&&) noexcept;
57
58 ValueBuilder(const ValueBuilder& other);
59 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
60 ValueBuilder(ValueBuilder&& other);
61 ValueBuilder& operator=(const ValueBuilder& other);
62 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
63 ValueBuilder& operator=(ValueBuilder&& other);
64
65 ValueBuilder(const formats::json::Value& other);
66 ValueBuilder(formats::json::Value&& other);
67
68 /// @name Concrete type constructors
69 /// @{
70 ValueBuilder(std::nullptr_t)
71 : ValueBuilder()
72 {}
73 ValueBuilder(bool t);
74 ValueBuilder(const char* str);
75 ValueBuilder(char* str);
76 ValueBuilder(const std::string& str);
77 ValueBuilder(std::string_view str);
78 ValueBuilder(int t);
79 ValueBuilder(unsigned int t);
80 ValueBuilder(uint64_t t);
81 ValueBuilder(int64_t t);
82 ValueBuilder(float t);
83 ValueBuilder(double t);
84 /// @}
85
86 /// Universal constructor using Serialize
87 template <typename T>
88 ValueBuilder(const T& t)
89 : ValueBuilder(DoSerialize(t))
90 {}
91
92 /// @brief Access member by key for modification.
93 /// @throw `TypeMismatchException` if not object or null value.
94 ValueBuilder operator[](std::string key);
95 /// @brief Access array member by index for modification.
96 /// @throw `TypeMismatchException` if not an array value.
97 /// @throw `OutOfBoundsException` if index is greater than size.
98 ValueBuilder operator[](std::size_t index);
99 /// @brief Access member by key for modification.
100 /// @throw `TypeMismatchException` if not object or null value.
101 template <typename Tag, utils::StrongTypedefOps Ops>
102 requires(utils::IsStrongTypedefLoggable(Ops))
103 ValueBuilder operator[](utils::StrongTypedef<Tag, std::string, Ops> key) {
104 return (*this)[std::move(key.GetUnderlying())];
105 }
106
107 /// @brief Emplaces new member w/o a check whether the key already exists.
108 /// @warning May create invalid JSON with duplicate key.
109 /// @throw `TypeMismatchException` if not object or null value.
110 void EmplaceNocheck(std::string_view key, ValueBuilder value);
111
112 /// @brief Remove key from object. If key is missing nothing happens.
113 /// @throw `TypeMismatchException` if value is not an object.
114 void Remove(std::string_view key);
115
116 iterator begin();
117 iterator end();
118
119 /// @brief Returns whether the array or object is empty.
120 /// @throw `TypeMismatchException` if not an array or an object.
121 bool IsEmpty() const;
122
123 /// @brief Returns true if *this holds a Null (Type::kNull).
124 bool IsNull() const noexcept;
125
126 /// @brief Returns true if *this is convertible to bool.
127 bool IsBool() const noexcept;
128
129 /// @brief Returns true if *this is convertible to int.
130 bool IsInt() const noexcept;
131
132 /// @brief Returns true if *this is convertible to int64_t.
133 bool IsInt64() const noexcept;
134
135 /// @brief Returns true if *this is convertible to uint64_t.
136 bool IsUInt64() const noexcept;
137
138 /// @brief Returns true if *this is convertible to double.
139 bool IsDouble() const noexcept;
140
141 /// @brief Returns true if *this is convertible to std::string.
142 bool IsString() const noexcept;
143
144 /// @brief Returns true if *this is an array (Type::kArray).
145 bool IsArray() const noexcept;
146
147 /// @brief Returns true if *this holds a map (Type::kObject).
148 bool IsObject() const noexcept;
149
150 /// @brief Returns array size or object members count.
151 /// @throw `TypeMismatchException` if not an array or an object.
152 std::size_t GetSize() const;
153
154 /// @brief Returns storage capacity for array elements or object members.
155 /// @throw `TypeMismatchException` if not an array or an object.
156 std::size_t GetCapacity() const;
157
158 /// @brief Returns true if value holds a `key`.
159 /// @throw `TypeMismatchException` if `*this` is not a map or null.
160 bool HasMember(std::string_view key) const;
161
162 /// @brief Returns full path to this value.
163 std::string GetPath() const;
164
165 /// @brief Ensures storage capacity for array elements or object members is at least `capacity` without changing
166 /// size.
167 /// @throw `TypeMismatchException` if not an array or object.
168 void Reserve(std::size_t capacity);
169
170 /// @brief Resize the array value or convert null value
171 /// into an array of requested size.
172 /// @throw `TypeMismatchException` if not an array or null.
173 void Resize(std::size_t size);
174
175 /// @brief Add element into the last position of array.
176 /// @throw `TypeMismatchException` if not an array or null.
177 void PushBack(ValueBuilder&& bld);
178
179 /// @brief Take out the resulting `Value` object.
180 /// After calling this method the object is in unspecified
181 /// (but valid - possibly null) state.
182 /// @throw `json::Exception` if called not from the root builder.
184
185private:
186 class EmplaceEnabler {};
187
188public:
189 /// @cond
190 ValueBuilder(EmplaceEnabler, impl::MutableValueWrapper) noexcept;
191 /// @endcond
192
193private:
194 enum class CheckMemberExists { kYes, kNo };
195
196 explicit ValueBuilder(impl::MutableValueWrapper) noexcept;
197
198 static void Copy(impl::Value& to, const ValueBuilder& from);
199 static void Move(impl::Value& to, ValueBuilder&& from);
200
201 impl::Value& AddMember(std::string_view key, CheckMemberExists);
202
203 template <typename T>
204 USERVER_IMPL_NODEBUG_INLINE_FUNC static Value DoSerialize(const T& t) {
205 static_assert(
206 formats::common::impl::HasSerialize<Value, T>,
207 "There is no `Serialize(const T&, formats::serialize::To<json::Value>)` "
208 "in namespace of `T` or `formats::serialize`. "
209 ""
210 "Probably you forgot to include the <userver/formats/serialize/common_containers.hpp> header "
211 "or one of the <formats/json/serialize_*.hpp> headers or you have not provided a `Serialize` function "
212 "overload."
213 );
214
215 return Serialize(t, formats::serialize::To<Value>());
216 }
217
218 impl::MutableValueWrapper value_;
219
220 friend class Iterator<IterTraits, common::IteratorDirection::kForward>;
221 friend class Iterator<IterTraits, common::IteratorDirection::kReverse>;
222};
223
224template <typename T>
225requires(std::is_integral<T>::value && sizeof(T) <= sizeof(int64_t))
226Value Serialize(T value, formats::serialize::To<Value>) {
227 using Type = std::conditional_t<std::is_signed<T>::value, int64_t, uint64_t>;
228 return json::ValueBuilder(static_cast<Type>(value)).ExtractValue();
229}
230
231json::Value Serialize(std::chrono::system_clock::time_point tp, formats::serialize::To<Value>);
232
233/// Optimized maps of StrongTypedefs serialization for JSON
234template <typename T>
235requires(meta::IsUniqueMap<T> && utils::IsStrongTypedefLoggable(T::key_type::kOps))
236Value Serialize(const T& value, formats::serialize::To<Value>) {
237 json::ValueBuilder builder(formats::common::Type::kObject);
238 for (const auto& [key, value] : value) {
239 builder.EmplaceNocheck(key.GetUnderlying(), value);
240 }
241 return builder.ExtractValue();
242}
243
244/// Optimized maps serialization for JSON
245template <typename T>
246requires meta::IsUniqueMap<T> && std::is_convertible_v<typename T::key_type, std::string>
247Value Serialize(const T& value, formats::serialize::To<Value>) {
248 json::ValueBuilder builder(formats::common::Type::kObject);
249 for (const auto& [key, value] : value) {
250 builder.EmplaceNocheck(key, value);
251 }
252 return builder.ExtractValue();
253}
254
255} // namespace formats::json
256
257USERVER_NAMESPACE_END