diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 0efb7433d..42d3b9fa7 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -15,10 +15,7 @@ using Newtonsoft.Json.Serialization; namespace FlaxEngine.Json { - /// - /// Objects serialization tool (json format). - /// - public static class JsonSerializer + partial class JsonSerializer { internal class SerializerCache { diff --git a/Source/Engine/Serialization/JsonSerializer.h b/Source/Engine/Serialization/JsonSerializer.h new file mode 100644 index 000000000..fd831cfc2 --- /dev/null +++ b/Source/Engine/Serialization/JsonSerializer.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/ISerializable.h" +#include "Engine/Core/Types/Span.h" +#include "Engine/Core/Collections/Array.h" + +/// +/// Objects serialization tool (json format). +/// +API_CLASS(Static, Namespace="FlaxEngine.Json") class FLAXENGINE_API JsonSerializer +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(JsonSerializer); + + /// + /// Performs object Json serialization to the raw bytes. + /// + /// The object to serialize (can be null). + /// The output data. + API_FUNCTION() static Array SaveToBytes(ISerializable* obj); + + /// + /// Performs object Json deserialization from the raw bytes. + /// + /// The object to deserialize (can be null). + /// The source data to read from. + /// The engine build number of the saved data. Used to resolve old object formats when loading deprecated data. + API_FUNCTION() static void LoadFromBytes(ISerializable* obj, const Span& data, int32 engineBuild); +}; diff --git a/Source/Engine/Serialization/ReadStream.h b/Source/Engine/Serialization/ReadStream.h index f7b77b0d5..5910ac72d 100644 --- a/Source/Engine/Serialization/ReadStream.h +++ b/Source/Engine/Serialization/ReadStream.h @@ -8,6 +8,7 @@ struct CommonValue; struct Variant; struct VariantType; +class ISerializable; /// /// Base class for all data read streams @@ -204,6 +205,13 @@ public: ReadBytes(data->Get(), size * sizeof(T)); } + /// + /// Deserializes object from Json by reading it as a raw data (ver+length+bytes). + /// + /// Reads version number, data length and actual data bytes from the stream. + /// The object to deserialize. + void ReadJson(ISerializable* obj); + public: // [Stream] diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index ce1538a36..067c5e269 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -3,11 +3,16 @@ #include "ReadStream.h" #include "WriteStream.h" #include "JsonWriters.h" +#include "JsonSerializer.h" +#include "MemoryReadStream.h" #include "Engine/Core/Types/CommonValue.h" #include "Engine/Core/Types/Variant.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Content/Asset.h" +#include "Engine/Core/Cache.h" #include "Engine/Debug/DebugLog.h" +#include "Engine/Debug/Exceptions/JsonParseException.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/ManagedSerialization.h" #include "Engine/Scripting/Scripting.h" #include "Engine/Scripting/ScriptingObject.h" @@ -489,6 +494,29 @@ void ReadStream::ReadVariant(Variant* data) } } +void ReadStream::ReadJson(ISerializable* obj) +{ + int32 engineBuild, size; + ReadInt32(&engineBuild); + ReadInt32(&size); + if (obj) + { + if (const auto memoryStream = dynamic_cast(this)) + { + JsonSerializer::LoadFromBytes(obj, Span((byte*)memoryStream->Read(size), size), engineBuild); + } + else + { + void* data = Allocator::Allocate(size); + ReadBytes(data, size); + JsonSerializer::LoadFromBytes(obj, Span((byte*)data, size), engineBuild); + Allocator::Free(data); + } + } + else + SetPosition(GetPosition() + size); +} + void WriteStream::WriteText(const StringView& text) { for (int32 i = 0; i < text.Length(); i++) @@ -734,3 +762,57 @@ void WriteStream::WriteVariant(const Variant& data) CRASH; } } + +void WriteStream::WriteJson(ISerializable* obj, const void* otherObj) +{ + WriteInt32(FLAXENGINE_VERSION_BUILD); + if (obj) + { + rapidjson_flax::StringBuffer buffer; + CompactJsonWriter writer(buffer); + writer.StartObject(); + obj->Serialize(writer, otherObj); + writer.EndObject(); + + WriteInt32((int32)buffer.GetSize()); + WriteBytes((byte*)buffer.GetString(), (int32)buffer.GetSize()); + } + else + WriteInt32(0); +} + +Array JsonSerializer::SaveToBytes(ISerializable* obj) +{ + Array result; + if (obj) + { + rapidjson_flax::StringBuffer buffer; + CompactJsonWriter writer(buffer); + writer.StartObject(); + obj->Serialize(writer, nullptr); + writer.EndObject(); + result.Set((byte*)buffer.GetString(), (int32)buffer.GetSize()); + } + return result; +} + +void JsonSerializer::LoadFromBytes(ISerializable* obj, const Span& data, int32 engineBuild) +{ + if (!obj || data.Length() == 0) + return; + + ISerializable::SerializeDocument document; + { + PROFILE_CPU_NAMED("Json.Parse"); + document.Parse((const char*)data.Get(), data.Length()); + } + if (document.HasParseError()) + { + Log::JsonParseException(document.GetParseError(), document.GetErrorOffset()); + return; + } + + auto modifier = Cache::ISerializeModifier.Get(); + modifier->EngineBuild = engineBuild; + obj->Deserialize(document, modifier.Value); +} diff --git a/Source/Engine/Serialization/WriteStream.h b/Source/Engine/Serialization/WriteStream.h index 94e19fbba..a189c9714 100644 --- a/Source/Engine/Serialization/WriteStream.h +++ b/Source/Engine/Serialization/WriteStream.h @@ -8,6 +8,7 @@ struct CommonValue; struct Variant; struct VariantType; +class ISerializable; /// /// Base class for all data write streams @@ -240,6 +241,14 @@ public: WriteBytes(data.Get(), size * sizeof(T)); } + /// + /// Serializes object to Json and writes it as a raw data (ver+length+bytes). + /// + /// Writes version number, data length and actual data bytes to the stream. + /// The object to serialize. + /// The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties. + void WriteJson(ISerializable* obj, const void* otherObj = nullptr); + public: // [Stream] diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index f0c9d4452..d8e102981 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -357,6 +357,26 @@ namespace FlaxEngine return new Matrix(stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle(), stream.ReadSingle()); } + /// + /// Deserializes object from Json by reading it as a raw data (ver+length+bytes). + /// + /// Reads version number, data length and actual data bytes from the stream. + /// The stream. + /// The object to deserialize. + public static void ReadJson(this BinaryReader stream, ISerializable obj) + { + // ReadStream::ReadJson + var engineBuild = stream.ReadInt32(); + var size = stream.ReadInt32(); + if (obj != null) + { + var data = stream.ReadBytes(size); + Json.JsonSerializer.LoadFromBytes(obj, data, engineBuild); + } + else + stream.BaseStream.Seek(size, SeekOrigin.Current); + } + /// /// Writes the color to the binary stream. /// @@ -548,5 +568,25 @@ namespace FlaxEngine stream.Write(value.M43); stream.Write(value.M44); } + + /// + /// Serializes object to Json and writes it as a raw data (ver+length+bytes). + /// + /// The stream. + /// Writes version number, data length and actual data bytes to the stream. + /// The object to serialize. + public static void WriteJson(this BinaryWriter stream, ISerializable obj) + { + // WriteStream::WriteJson + stream.Write(Globals.EngineBuildNumber); + if (obj != null) + { + var bytes = Json.JsonSerializer.SaveToBytes(obj); + stream.Write(bytes.Length); + stream.Write(bytes); + } + else + stream.Write(0); + } } }