Implement JSON difference serialization
This commit is contained in:
@@ -37,7 +37,11 @@ namespace FlaxEngine.Json
|
||||
{
|
||||
// Skip serialization as reference id for the root object serialization (eg. Script)
|
||||
var cache = JsonSerializer.Current.Value;
|
||||
#if !USE_NETCORE
|
||||
if (cache != null && cache.IsDuringSerialization && cache.SerializerWriter.SerializeStackSize == 0)
|
||||
#else
|
||||
if (cache != null && cache.IsDuringSerialization)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -142,6 +146,7 @@ namespace FlaxEngine.Json
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
#if !USE_NETCORE
|
||||
/// <inheritdoc />
|
||||
public override void WriteJsonDiff(JsonWriter writer, object value, object other, Newtonsoft.Json.JsonSerializer serializer)
|
||||
{
|
||||
@@ -170,6 +175,7 @@ namespace FlaxEngine.Json
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
|
||||
@@ -232,8 +238,10 @@ namespace FlaxEngine.Json
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => true;
|
||||
|
||||
#if !USE_NETCORE
|
||||
/// <inheritdoc />
|
||||
public override bool CanWriteDiff => true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -11,7 +12,7 @@ using FlaxEngine.Json.JsonCustomSerializers;
|
||||
using FlaxEngine.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace FlaxEngine.Json
|
||||
{
|
||||
@@ -23,7 +24,11 @@ namespace FlaxEngine.Json
|
||||
public StringBuilder StringBuilder;
|
||||
public StringWriter StringWriter;
|
||||
public JsonTextWriter JsonWriter;
|
||||
#if !USE_NETCORE
|
||||
public JsonSerializerInternalWriter SerializerWriter;
|
||||
#else
|
||||
public /*JsonSerializerInternalWriter*/ object SerializerWriter;
|
||||
#endif
|
||||
public UnmanagedMemoryStream MemoryStream;
|
||||
public StreamReader Reader;
|
||||
public bool IsDuringSerialization;
|
||||
@@ -32,9 +37,18 @@ namespace FlaxEngine.Json
|
||||
{
|
||||
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
|
||||
JsonSerializer.Formatting = Formatting.Indented;
|
||||
#if USE_NETCORE
|
||||
Type jsonSerializerInternalWriterType =
|
||||
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
|
||||
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
|
||||
System.Reflection.ConstructorInfo ctor = jsonSerializerInternalWriterType.GetConstructors
|
||||
(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)[0];
|
||||
SerializerWriter = ctor.Invoke(new object[] { JsonSerializer });
|
||||
#else
|
||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||
#endif
|
||||
StringBuilder = new StringBuilder(256);
|
||||
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
|
||||
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
|
||||
MemoryStream = new UnmanagedMemoryStream((byte*)0, 0);
|
||||
Reader = new StreamReader(MemoryStream, Encoding.UTF8, false);
|
||||
JsonWriter = new JsonTextWriter(StringWriter)
|
||||
@@ -107,7 +121,72 @@ namespace FlaxEngine.Json
|
||||
return sceneObjA.PrefabObjectID == sceneObjB.PrefabObjectID;
|
||||
}*/
|
||||
|
||||
return Newtonsoft.Json.Utilities.MiscellaneousUtils.DefaultValueEquals(objA, objB);
|
||||
// Based on Newtonsoft.Json MiscellaneousUtils-class ValueEquals-method
|
||||
bool DefaultValueEquals(object objA_, object objB_)
|
||||
{
|
||||
bool IsInteger(object value)
|
||||
{
|
||||
var type = value.GetType();
|
||||
return type == typeof(SByte) ||
|
||||
type == typeof(Byte) ||
|
||||
type == typeof(Int16) ||
|
||||
type == typeof(UInt16) ||
|
||||
type == typeof(Int32) ||
|
||||
type == typeof(UInt32) ||
|
||||
type == typeof(Int64) ||
|
||||
type == typeof(SByte) ||
|
||||
type == typeof(UInt64);
|
||||
}
|
||||
|
||||
if (objA_ == objB_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (objA_ == null || objB_ == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comparing an Int32 and Int64 both of the same value returns false
|
||||
// make types the same then compare
|
||||
if (objA_.GetType() != objB_.GetType())
|
||||
{
|
||||
if (IsInteger(objA_) && IsInteger(objB_))
|
||||
{
|
||||
return Convert.ToDecimal(objA_, CultureInfo.CurrentCulture).Equals(Convert.ToDecimal(objB_, CultureInfo.CurrentCulture));
|
||||
}
|
||||
else if ((objA_ is double || objA_ is float || objA_ is decimal) && (objB_ is double || objB_ is float || objB_ is decimal))
|
||||
{
|
||||
return Mathd.NearEqual(Convert.ToDouble(objA_, CultureInfo.CurrentCulture), Convert.ToDouble(objB_, CultureInfo.CurrentCulture));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Diff on collections
|
||||
if (objA_ is System.Collections.IList aList && objB_ is System.Collections.IList bList)
|
||||
{
|
||||
if (aList.Count != bList.Count)
|
||||
return false;
|
||||
}
|
||||
if (objA_ is System.Collections.IEnumerable aEnumerable && objB_ is System.Collections.IEnumerable bEnumerable)
|
||||
{
|
||||
var aEnumerator = aEnumerable.GetEnumerator();
|
||||
var bEnumerator = bEnumerable.GetEnumerator();
|
||||
while (aEnumerator.MoveNext())
|
||||
{
|
||||
if (!bEnumerator.MoveNext() || !ValueEquals(aEnumerator.Current, bEnumerator.Current))
|
||||
return false;
|
||||
}
|
||||
return !bEnumerator.MoveNext();
|
||||
}
|
||||
|
||||
return objA_.Equals(objB_);
|
||||
}
|
||||
|
||||
return /*Newtonsoft.Json.Utilities.MiscellaneousUtils.*/DefaultValueEquals(objA, objB);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -124,7 +203,17 @@ namespace FlaxEngine.Json
|
||||
|
||||
cache.StringBuilder.Clear();
|
||||
cache.IsDuringSerialization = true;
|
||||
#if !USE_NETCORE
|
||||
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
||||
#else
|
||||
Type jsonSerializerInternalWriterType =
|
||||
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
|
||||
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
|
||||
|
||||
System.Reflection.MethodInfo Serialize = jsonSerializerInternalWriterType.GetMethod("Serialize",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
|
||||
Serialize.Invoke(cache.SerializerWriter, new object[] { cache.JsonWriter, obj, type });
|
||||
#endif
|
||||
|
||||
return cache.StringBuilder.ToString();
|
||||
}
|
||||
@@ -143,7 +232,17 @@ namespace FlaxEngine.Json
|
||||
|
||||
cache.StringBuilder.Clear();
|
||||
cache.IsDuringSerialization = true;
|
||||
#if !USE_NETCORE
|
||||
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
|
||||
#else
|
||||
Type jsonSerializerInternalWriterType =
|
||||
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
|
||||
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
|
||||
|
||||
System.Reflection.MethodInfo Serialize = jsonSerializerInternalWriterType.GetMethod("Serialize",
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
|
||||
Serialize.Invoke(cache.SerializerWriter, new object[] { cache.JsonWriter, obj, type });
|
||||
#endif
|
||||
|
||||
return cache.StringBuilder.ToString();
|
||||
}
|
||||
@@ -157,15 +256,25 @@ namespace FlaxEngine.Json
|
||||
/// <returns>The output json string.</returns>
|
||||
public static string SerializeDiff(object obj, object other, bool isManagedOnly = false)
|
||||
{
|
||||
Type type = obj.GetType();
|
||||
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
|
||||
Current.Value = cache;
|
||||
|
||||
cache.StringBuilder.Clear();
|
||||
cache.IsDuringSerialization = true;
|
||||
cache.SerializerWriter.SerializeDiff(cache.JsonWriter, obj, type, other);
|
||||
|
||||
return cache.StringBuilder.ToString();
|
||||
JObject jObj = JObject.FromObject(obj, cache.JsonSerializer);
|
||||
JObject jOther = JObject.FromObject(other, cache.JsonSerializer);
|
||||
JObject diff = new JObject();
|
||||
foreach (KeyValuePair<string, JToken> prop in jObj)
|
||||
{
|
||||
JProperty otherProp = jOther.Property(prop.Key);
|
||||
if (JToken.DeepEquals(prop.Value, otherProp.Value))
|
||||
continue;
|
||||
|
||||
diff.Add(prop.Key, prop.Value);
|
||||
}
|
||||
|
||||
return diff.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user