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