Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
serialization.h
1// SPDX-FileCopyrightText: © 2024-2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <memory>
7#include <string>
8
9#include "utils/exceptions.h"
10#include "utils/qt.h"
11#include "utils/traits.h"
12#include "utils/types.h"
13
14#include <QUuid>
15
16#include <au/au.hh>
17#include <boost/unordered/concurrent_flat_map_fwd.hpp>
18#include <nlohmann/json.hpp>
19
21
22inline void
23to_json (nlohmann::json &j, const QString &s)
24{
25 j = s.toStdString ();
26}
27inline void
28from_json (const nlohmann::json &j, QString &s)
29{
30 s = QString::fromUtf8 (j.get<std::string> ());
31}
32
33inline void
34to_json (nlohmann::json &j, const QUuid &uuid)
35{
36 j = uuid.toString (QUuid::WithoutBraces);
37}
38inline void
39from_json (const nlohmann::json &j, QUuid &uuid)
40{
41 uuid = QUuid::fromString (j.get<QString> ());
42}
43
44namespace zrythm::utils::serialization
45{
46
47static constexpr auto kSchemaVersionKey = "schemaVersion"sv;
48static constexpr auto kAppVersionKey = "appVersion"sv;
49static constexpr auto kDocumentTypeKey = "documentType"sv;
50static constexpr auto kVariantTypeKey = "type"sv;
51static constexpr auto kVariantNonObjectValueKey = "nonObjectValue"sv;
52
67template <StdVariant Variant, ObjectBuilder BuilderT>
68inline void
69variant_create_object_only (
70 const nlohmann::json &j,
71 Variant &var,
72 const BuilderT &builder)
73{
74 const auto index =
75 j.at (zrythm::utils::serialization::kVariantTypeKey).get<int> ();
76
77 auto creator = [&]<size_t... I> (std::index_sequence<I...>) {
78 Variant result{};
79
80 auto create_type_if_current_index = [&]<size_t N> () {
81 using Type = std::variant_alternative_t<N, Variant>;
82 using StrippedType = std::remove_pointer_t<Type>;
83 if (N == index)
84 {
85 auto object_ptr = builder.template build<StrippedType> ();
86
87 if constexpr (std::is_pointer_v<Type>)
88 result = object_ptr.release ();
89 else if constexpr (std::is_copy_constructible_v<Type>)
90 result = *object_ptr;
91 else if constexpr (is_derived_from_template_v<QObjectUniquePtr, Type>)
92 result = std::move (*object_ptr);
93 else
94 {
95 DEBUG_TEMPLATE_PARAM (Type)
96 static_assert (
97 false,
98 "Only variant of pointers or copy-constructible types is currently supported");
99 }
100 }
101 };
102
103 (create_type_if_current_index.template operator()<I> (), ...);
104
105 return result;
106 };
107
108 var = creator (std::make_index_sequence<std::variant_size_v<Variant>>{});
109}
110
121template <StdVariant Variant>
122inline void
123variant_deserialize_data (const nlohmann::json &j, Variant &var)
124{
125 // Create a copy of j without the "type" field for value deserialization
126 nlohmann::json value_json = j;
127 value_json.erase (zrythm::utils::serialization::kVariantTypeKey);
128
129 std::visit (
130 [&] (auto &&obj) {
131 using ObjType = std::decay_t<decltype (obj)>;
132 if constexpr (std::is_pointer_v<ObjType>)
133 {
134 if (value_json.contains (kVariantNonObjectValueKey))
135 value_json[kVariantNonObjectValueKey].get_to (*obj);
136 else
137 value_json.get_to (*obj);
138 }
139 else if constexpr (is_derived_from_template_v<QObjectUniquePtr, ObjType>)
140 {
141 if (value_json.contains (kVariantNonObjectValueKey))
142 value_json[kVariantNonObjectValueKey].get_to (*obj);
143 else
144 value_json.get_to (*obj);
145 }
146 else
147 {
148 if (value_json.contains (kVariantNonObjectValueKey))
149 value_json[kVariantNonObjectValueKey].get_to (obj);
150 else
151 value_json.get_to (obj);
152 }
153 },
154 var);
155}
156
171template <StdVariant Variant, ObjectBuilder BuilderT>
172inline void
173variant_from_json_with_builder (
174 const nlohmann::json &j,
175 Variant &var,
176 const BuilderT &builder)
177{
178 variant_create_object_only (j, var, builder);
179 variant_deserialize_data (j, var);
180}
181
182}; // namespace zrythm::utils::serialization
183
184namespace nlohmann
185{
187template <typename T> struct adl_serializer<T *>
188{
189 static void to_json (json &j, const T * ptr)
190 {
191 if (ptr)
192 j = *ptr;
193 else
194 j = nullptr;
195 }
196 // from_json would be unsafe due to raw memory allocation, so handle each case
197 // manually in each type
198};
199
201template <typename... Args> struct adl_serializer<std::variant<Args...>>
202{
203 static void to_json (json &j, std::variant<Args...> const &v)
204 {
205 std::visit (
206 [&] (auto &&value) {
207 j[zrythm::utils::serialization::kVariantTypeKey] = v.index ();
208 // Merge the value's JSON fields directly into j
209 json value_json = std::forward<decltype (value)> (value);
210 if (value_json.is_object ())
211 {
212 j.update (value_json);
213 }
214 else
215 {
216 j[zrythm::utils::serialization::kVariantNonObjectValueKey] =
217 value_json;
218 }
219 },
220 v);
221 }
222};
223
225template <typename T> struct adl_serializer<std::unique_ptr<T>>
226{
227 static void to_json (json &json_value, const std::unique_ptr<T> &ptr)
228 {
229 if (ptr.get ())
230 {
231 json_value = *ptr;
232 }
233 else
234 {
235 json_value = nullptr;
236 }
237 }
238 static void from_json (const json &json_value, std::unique_ptr<T> &ptr)
239 requires std::is_default_constructible_v<T>
240 {
241 ptr = std::make_unique<T> ();
242 json_value.get_to (*ptr);
243 }
244};
245
247template <typename T> struct adl_serializer<std::shared_ptr<T>>
248{
249 static void to_json (json &json_value, const std::shared_ptr<T> &ptr)
250 {
251 if (ptr.get ())
252 {
253 json_value = *ptr;
254 }
255 else
256 {
257 json_value = nullptr;
258 }
259 }
260 static void from_json (const json &json_value, std::shared_ptr<T> &ptr)
261 requires std::is_default_constructible_v<T>
262 {
263 ptr = std::make_shared<T> ();
264 json_value.get_to (*ptr);
265 }
266};
267
269template <typename T> struct adl_serializer<zrythm::utils::QObjectUniquePtr<T>>
270{
271 static void
272 to_json (json &json_value, const zrythm::utils::QObjectUniquePtr<T> &ptr)
273 {
274 if (ptr.get ())
275 {
276 json_value = *ptr;
277 }
278 else
279 {
280 json_value = nullptr;
281 }
282 }
283 static void
284 from_json (const json &json_value, zrythm::utils::QObjectUniquePtr<T> &ptr)
285 requires std::is_default_constructible_v<T>
286 {
287 ptr = new T ();
288 json_value.get_to (*ptr);
289 }
290};
291
293template <StrongTypedef T> struct adl_serializer<T>
294{
295 static void to_json (json &json_value, const T &strong_type)
296 {
297 json_value = type_safe::get (strong_type);
298 }
299 static void from_json (const json &json_value, T &strong_type)
300 {
301 json_value.get_to (type_safe::get (strong_type));
302 }
303};
304
305template <typename Key, typename T>
306struct adl_serializer<boost::unordered::concurrent_flat_map<Key, T>>
307{
308 using ConcurrentHashMap = boost::unordered::concurrent_flat_map<Key, T>;
309 // convert to std::map since nlohmann::json knows how to serialize it
310 static void to_json (json &j, const ConcurrentHashMap &map)
311 {
312 std::map<Key, T> temp;
313 map.visit_all ([&temp] (const auto &kv) {
314 temp.emplace (kv.first, kv.second);
315 });
316 j = temp;
317 }
318
319 static void from_json (const json &j, ConcurrentHashMap &map)
320 {
321 auto temp = j.get<std::map<Key, T>> (); // Deserialize to std::map first
322 map.clear ();
323 for (const auto &kv : temp)
324 {
325 map.emplace (kv.first, kv.second);
326 }
327 }
328};
329
331template <typename Unit, typename Rep>
332struct adl_serializer<au::Quantity<Unit, Rep>>
333{
334 static void to_json (json &j, const au::Quantity<Unit, Rep> &quantity)
335 {
336 j = quantity.in (Unit{});
337 }
338
339 static void from_json (const json &j, au::Quantity<Unit, Rep> &quantity)
340 {
341 Rep raw_value;
342 j.get_to (raw_value);
343 quantity = au::QuantityMaker<Unit>{}(raw_value);
344 }
345};
346
347} // namespace nlohmann
A unique pointer for QObject objects that also works with QObject-based ownership.
Definition qt.h:38
Base class for exceptions in Zrythm.
Definition exceptions.h:20