Loading...
Searching...
No Matches
juce_JSONSerialisation.h
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class ToVarOptions
{
public:
[[nodiscard]] ToVarOptions withExplicitVersion (std::optional<int> x) const { return withMember (*this, &ToVarOptions::explicitVersion, x); }
[[nodiscard]] ToVarOptions withVersionIncluded (bool x) const { return withMember (*this, &ToVarOptions::versionIncluded, x); }
[[nodiscard]] auto getExplicitVersion() const { return explicitVersion; }
[[nodiscard]] auto getVersionIncluded() const { return versionIncluded; }
private:
std::optional<std::optional<int>> explicitVersion;
bool versionIncluded = true;
};
class ToVar
{
public:
using Options = ToVarOptions;
template <typename T>
static std::optional<var> convert (const T& t, const Options& options = {})
{
return Visitor::convert (t, options);
}
private:
class Visitor
{
public:
template <typename T>
static std::optional<var> convert (const T& t, const Options& options)
{
constexpr auto fallbackVersion = detail::ForwardingSerialisationTraits<T>::marshallingVersion;
const auto versionToUse = options.getExplicitVersion()
.value_or (fallbackVersion);
if (versionToUse > fallbackVersion)
{
// The requested explicit version is higher than the declared version of the type.
return std::nullopt;
}
Visitor visitor { versionToUse, options.getVersionIncluded() };
detail::doSave (visitor, t);
return visitor.value;
}
std::optional<int> getVersion() const { return version; }
template <typename... Ts>
void operator() (Ts&&... ts)
{
(visit (std::forward<Ts> (ts)), ...);
}
private:
Visitor (const std::optional<int>& explicitVersion, bool includeVersion)
: version (explicitVersion),
value ([&]() -> var
{
if (! (version.has_value() && includeVersion))
return var();
auto obj = std::make_unique<DynamicObject>();
obj->setProperty ("__version__", *version);
return obj.release();
}()),
versionIncluded (includeVersion) {}
template <typename T>
void visit (const T& t)
{
if constexpr (std::is_integral_v<T>)
{
push ((int64) t);
}
else if constexpr (std::is_floating_point_v<T>)
{
push ((double) t);
}
else if (auto converted = convert (t))
{
push (*converted);
}
else
{
value.reset();
}
}
template <typename T>
void visit (const Named<T>& named)
{
if (! value.has_value())
return;
if (value == var())
value = new DynamicObject;
auto* obj = value->getDynamicObject();
if (obj == nullptr)
{
// Serialisation failure! This may be caused by archiving a primitive or
// SerialisationSize, and then attempting to archive a named pair to the same
// archive instance.
// When using named pairs, *all* items serialised with a particular archiver must be
// named pairs.
jassertfalse;
value.reset();
return;
}
if (! trySetProperty (*obj, named))
value.reset();
}
template <typename T>
void visit (const SerialisationSize<T>&)
{
push (Array<var>{});
}
void visit (const bool& t)
{
push (t);
}
void visit (const String& t)
{
push (t);
}
void visit (const var& t)
{
push (t);
}
template <typename T>
std::optional<var> convert (const T& t)
{
return convert (t, Options{}.withVersionIncluded (versionIncluded));
}
void push (var v)
{
if (! value.has_value())
return;
if (*value == var())
*value = v;
else if (auto* array = value->getArray())
array->add (v);
else
value.reset();
}
template <typename T>
bool trySetProperty (DynamicObject& obj, const Named<T>& n)
{
if (const auto converted = convert (n.value))
{
obj.setProperty (Identifier (std::string (n.name)), *converted);
return true;
}
return false;
}
std::optional<int> version;
std::optional<var> value;
bool versionIncluded = true;
};
};
//==============================================================================
class FromVar
{
public:
template <typename T>
static std::optional<T> convert (const var& v)
{
return Visitor::convert<T> (v);
}
private:
class Visitor
{
public:
template <typename T>
static std::optional<T> convert (const var& v)
{
const auto version = [&]() -> std::optional<int>
{
if (auto* obj = v.getDynamicObject())
if (obj->hasProperty ("__version__"))
return (int) obj->getProperty ("__version__");
return std::nullopt;
}();
Visitor visitor { version, v };
T t{};
detail::doLoad (visitor, t);
return ! visitor.failed ? std::optional<T> (std::move (t))
: std::nullopt;
}
std::optional<int> getVersion() const { return version; }
template <typename... Ts>
void operator() (Ts&&... ts)
{
(visit (std::forward<Ts> (ts)), ...);
}
private:
Visitor (std::optional<int> vn, const var& i)
: version (vn), input (i) {}
template <typename T>
void visit (T& t)
{
if constexpr (std::is_integral_v<T>)
{
readPrimitive (std::in_place_type<int64>, t);
}
else if constexpr (std::is_floating_point_v<T>)
{
readPrimitive (std::in_place_type<double>, t);
}
else
{
auto node = getNodeToRead();
if (! node.has_value())
return;
auto converted = convert<T> (*node);
if (converted.has_value())
t = *converted;
else
failed = true;
}
}
template <typename T>
void visit (const Named<T>& named)
{
auto node = getNodeToRead();
if (! node.has_value())
return;
auto* obj = node->getDynamicObject();
failed = obj == nullptr || ! tryGetProperty (*obj, named);
}
template <typename T>
void visit (const SerialisationSize<T>& t)
{
if (failed)
return;
if (auto* array = input.getArray())
{
t.size = static_cast<T> (array->size());
currentArrayIndex = 0;
}
else
{
failed = true;
}
}
void visit (bool& t)
{
readPrimitive (std::in_place_type<bool>, t);
}
void visit (String& t)
{
readPrimitive (std::in_place_type<String>, t);
}
void visit (var& t)
{
t = input;
}
static std::optional<double> pullTyped (std::in_place_type_t<double>, const var& source)
{
return source.isDouble() ? std::optional<double> ((double) source) : std::nullopt;
}
static std::optional<int64> pullTyped (std::in_place_type_t<int64>, const var& source)
{
return source.isInt() || source.isInt64() ? std::optional<int64> ((int64) source) : std::nullopt;
}
static std::optional<bool> pullTyped (std::in_place_type_t<bool>, const var& source)
{
return std::optional<bool> ((bool) source);
}
static std::optional<String> pullTyped (std::in_place_type_t<String>, const var& source)
{
return source.isString() ? std::optional<String> (source.toString()) : std::nullopt;
}
std::optional<var> getNodeToRead()
{
if (failed)
return std::nullopt;
if (currentArrayIndex == std::numeric_limits<size_t>::max())
return input;
const auto* array = input.getArray();
if (array == nullptr)
return input;
if ((int) currentArrayIndex < array->size())
return array->getReference ((int) currentArrayIndex++);
failed = true;
return std::nullopt;
}
template <typename TypeToRead, typename T>
void readPrimitive (std::in_place_type_t<TypeToRead> tag, T& t)
{
auto node = getNodeToRead();
if (! node.has_value())
return;
auto typed = pullTyped (tag, *node);
if (typed.has_value())
t = static_cast<T> (*typed);
else
failed = true;
}
template <typename T>
static bool tryGetProperty (const DynamicObject& obj, const Named<T>& n)
{
const Identifier identifier (String (n.name.data(), n.name.size()));
if (! obj.hasProperty (identifier))
return false;
const auto converted = convert<T> (obj.getProperty (identifier));
if (! converted.has_value())
return false;
n.value = *converted;
return true;
}
std::optional<int> version;
var input;
size_t currentArrayIndex = std::numeric_limits<size_t>::max();
bool failed = false;
};
};
//==============================================================================
template <typename Type>
struct VariantConverter
{
static Type fromVar (const var& v)
{
return static_cast<Type> (v);
}
static var toVar (const Type& t)
{
return t;
}
};
#ifndef DOXYGEN
template <>
struct VariantConverter<String>
{
static String fromVar (const var& v) { return v.toString(); }
static var toVar (const String& s) { return s; }
};
#endif
template <typename Type>
struct StrictVariantConverter
{
static_assert (detail::serialisationKind<Type> != detail::SerialisationKind::none);
static Type fromVar (const var& v)
{
auto converted = FromVar::convert<Type> (v);
jassert (converted.has_value());
return std::move (converted).value_or (Type{});
}
static var toVar (const Type& t)
{
auto converted = ToVar::convert<> (t);
jassert (converted.has_value());
return std::move (converted).value_or (var{});
}
};
} // namespace juce
juce::FromVar
Definition juce_JSONSerialisation.h:260
juce::FromVar::convert
static std::optional< T > convert(const var &v)
Definition juce_JSONSerialisation.h:267
juce::String
Definition juce_String.h:53
juce::ToVarOptions
Definition juce_JSONSerialisation.h:34
juce::ToVarOptions::withVersionIncluded
ToVarOptions withVersionIncluded(bool x) const
Definition juce_JSONSerialisation.h:48
juce::ToVarOptions::getVersionIncluded
auto getVersionIncluded() const
Definition juce_JSONSerialisation.h:54
juce::ToVarOptions::withExplicitVersion
ToVarOptions withExplicitVersion(std::optional< int > x) const
Definition juce_JSONSerialisation.h:40
juce::ToVarOptions::getExplicitVersion
auto getExplicitVersion() const
Definition juce_JSONSerialisation.h:51
juce::ToVar
Definition juce_JSONSerialisation.h:77
juce::ToVar::convert
static std::optional< var > convert(const T &t, const Options &options={})
Definition juce_JSONSerialisation.h:87
juce::var
Definition juce_Variant.h:42
juce::StrictVariantConverter
Definition juce_JSONSerialisation.h:516
juce::VariantConverter
Definition juce_JSONSerialisation.h:478