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);
+ }
}
}