Serialize and Deserialize Lists and 1D-Arrays

This commit is contained in:
Simon Lübeß
2024-01-01 13:03:34 +01:00
parent c111fff32b
commit 16f4dd349b
2 changed files with 168 additions and 50 deletions
+118 -37
View File
@@ -1,8 +1,10 @@
using GlitchyEngine.Core; using GlitchyEngine.Core;
using GlitchyEngine.Extensions; using GlitchyEngine.Extensions;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -82,20 +84,6 @@ internal class DeserializationObject
DeserializedClasses.Add(id, context); DeserializedClasses.Add(id, context);
ScriptGlue.Serialization_GetObjectTypeName(context._internalContext, out string fullTypeName);
Type type = GetTypeFromName(fullTypeName);
if (type == null)
return context;
context._instance = ActivatorExtension.CreateInstanceSafe(type);
if (context._instance == null)
return context;
context.DeserializeFields(context._instance);
return context; return context;
} }
@@ -127,6 +115,11 @@ internal class DeserializationObject
public EngineObjectReferenceHelper EngineObjectReference; public EngineObjectReferenceHelper EngineObjectReference;
} }
private T GetFieldValue<T>(string fieldName, SerializationType serializationType)
{
return (T)GetFieldValue(fieldName, serializationType);
}
private unsafe object GetFieldValue(string fieldName, SerializationType serializationType) private unsafe object GetFieldValue(string fieldName, SerializationType serializationType)
{ {
string completeFieldName = $"{_structScopeName}{fieldName}"; string completeFieldName = $"{_structScopeName}{fieldName}";
@@ -218,40 +211,43 @@ internal class DeserializationObject
if (!EntitySerializer.SerializeField(field)) if (!EntitySerializer.SerializeField(field))
continue; continue;
changed |= DeserializeField(obj, field); object currentValue = field.GetValue(obj);
object deserializeValue = DeserializeField(currentValue, field.FieldType, field.Name);
if (deserializeValue != NoValueDeserialized)
{
field.SetValue(obj, deserializeValue);
changed = true;
}
} }
return changed; return changed;
} }
private bool DeserializeField(object targetInstance, FieldInfo field) private object DeserializeField(object fieldValue, Type fieldType, string fieldName)
{ {
Type fieldType = field.FieldType;
object newFieldValue = NoValueDeserialized;
if (fieldType.IsPrimitive) if (fieldType.IsPrimitive)
{ {
newFieldValue = DeserializePrimitive(field.Name, fieldType); return DeserializePrimitive(fieldName, fieldType);
} }
else if (fieldType == typeof(string)) else if (fieldType == typeof(string))
{ {
newFieldValue = GetFieldValue(field.Name, SerializationType.String); return GetFieldValue(fieldName, SerializationType.String);
} }
else if (fieldType.IsEnum) else if (fieldType.IsEnum)
{ {
newFieldValue = DeserializeEnum(field.Name, fieldType); return DeserializeEnum(fieldName, fieldType);
} }
else if (fieldType.IsArray) else if (fieldType.IsArray)
{ {
// Log.Error($"Array serialization not yet implemented"); return DeserializeList(fieldName, fieldType, fieldType.GetElementType());
} }
else if (fieldType.IsGenericType) else if (fieldType.IsGenericType)
{ {
if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) if (fieldType.GetGenericTypeDefinition() == typeof(List<>))
{ {
//SerializeList(fieldName, type, o); return DeserializeList(fieldName, fieldType, fieldType.GetGenericArguments()[0]);
Log.Error($"List serialization not yet implemented");
} }
//else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) //else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
//{ //{
@@ -265,26 +261,18 @@ internal class DeserializationObject
} }
else if (fieldType.IsValueType) else if (fieldType.IsValueType)
{ {
object structValue = field.GetValue(targetInstance); return DeserializeStruct(fieldName, fieldValue);
newFieldValue = DeserializeStruct(field.Name, structValue);
} }
else if (fieldType.IsClass) else if (fieldType.IsClass)
{ {
newFieldValue = DeserializeClass(field.Name, fieldType); return DeserializeClass(fieldName, fieldType);
} }
else else
{ {
Log.Error($"Encountered unhandled type \"{fieldType}\" while serializing."); Log.Error($"Encountered unhandled type \"{fieldType}\" while serializing.");
} }
if (newFieldValue != NoValueDeserialized) return NoValueDeserialized;
{
field.SetValue(targetInstance, newFieldValue);
return true;
}
return false;
} }
private object DeserializePrimitive(string fieldName, Type fieldType) private object DeserializePrimitive(string fieldName, Type fieldType)
@@ -327,6 +315,89 @@ internal class DeserializationObject
return GetFieldValue(fieldName, expectedType); return GetFieldValue(fieldName, expectedType);
} }
/// <summary>
/// Gets the type that was originally stored in the container, or null, if the type doesn't exist.
/// </summary>
public Type StoredType
{
get
{
ScriptGlue.Serialization_GetObjectTypeName(_internalContext, out string fullTypeName);
return GetTypeFromName(fullTypeName);
}
}
private object DeserializeList(string fieldName, Type fieldType, Type elementType)
{
UUID id = (UUID)GetFieldValue(fieldName, SerializationType.ObjectReference);
if (id == UUID.Zero)
return null;
// Get serialization container for the instance
DeserializationObject deserializedObject = GetDeserializedObject(id);
Type type = deserializedObject.StoredType;
if (type == null || !type.IsAssignableTo(fieldType))
return null;
int count = deserializedObject.GetFieldValue<int>("Count", SerializationType.Int32);
Array array = null;
IList list = null;
if (type.IsArray)
{
array = Array.CreateInstance(elementType, count);
deserializedObject._instance = array;
}
else
{
Debug.Assert(fieldType.IsAssignableTo(typeof(IList)));
if (type.GetGenericTypeDefinition() == typeof(List<>))
{
list = (IList)ActivatorExtension.CreateInstanceSafe(type, count);
}
else
{
list = (IList)ActivatorExtension.CreateInstanceSafe(type);
}
if (list == null)
return null;
if (list.IsFixedSize)
{
Log.Error("Cannot deserialize fixed length lists.");
return null;
}
deserializedObject._instance = list;
}
for (int i = 0; i < count; i++)
{
object elementValue = ActivatorExtension.CreateInstanceSafe(elementType);
object newValue = deserializedObject.DeserializeField(elementValue, elementType, $"{i}");
if (newValue == NoValueDeserialized)
newValue = elementValue;
if (array != null)
array.SetValue(newValue, i);
else
list.Add(newValue);
}
return deserializedObject._instance;
}
private object DeserializeEnum(string fieldName, Type enumType) private object DeserializeEnum(string fieldName, Type enumType)
{ {
if (GetFieldValue(fieldName, SerializationType.Enum) is not string valueName) if (GetFieldValue(fieldName, SerializationType.Enum) is not string valueName)
@@ -399,8 +470,18 @@ internal class DeserializationObject
if (id == UUID.Zero) if (id == UUID.Zero)
return null; return null;
// Get serialization container for the instance
DeserializationObject deserializedObject = GetDeserializedObject(id); DeserializationObject deserializedObject = GetDeserializedObject(id);
Type type = deserializedObject.StoredType;
if (type == null)
return null;
deserializedObject._instance = ActivatorExtension.CreateInstanceSafe(type);
deserializedObject.DeserializeFields(deserializedObject._instance);
return deserializedObject._instance; return deserializedObject._instance;
} }
} }
+46 -9
View File
@@ -1,5 +1,6 @@
using GlitchyEngine.Core; using GlitchyEngine.Core;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
@@ -12,31 +13,31 @@ internal class SerializedObject
private UUID _id; private UUID _id;
private Dictionary<object, SerializedObject> _serializedClasses;
private Stack<string> _structScope = new(); private Stack<string> _structScope = new();
private string _structScopeName; private string _structScopeName;
public Dictionary<object, SerializedObject> SerializedClasses;
public SerializedObject(IntPtr internalContext, UUID id, Dictionary<object, SerializedObject> serializedClasses) public SerializedObject(IntPtr internalContext, UUID id, Dictionary<object, SerializedObject> serializedClasses)
{ {
_internalContext = internalContext; _internalContext = internalContext;
_id = id; _id = id;
SerializedClasses = serializedClasses; _serializedClasses = serializedClasses;
} }
private (SerializedObject context, bool newContext) GetSerializedObject(object o) private (SerializedObject context, bool newContext) GetSerializedObject(object o)
{ {
SerializedObject context; SerializedObject context;
if (SerializedClasses.TryGetValue(o, out context)) if (_serializedClasses.TryGetValue(o, out context))
return (context, false); return (context, false);
ScriptGlue.Serialization_CreateObject(_internalContext, o.GetType().FullName, out IntPtr contextPtr, out UUID id); ScriptGlue.Serialization_CreateObject(_internalContext, o.GetType().FullName, out IntPtr contextPtr, out UUID id);
context = new SerializedObject(contextPtr, id, SerializedClasses); context = new SerializedObject(contextPtr, id, _serializedClasses);
SerializedClasses.Add(o, context); _serializedClasses.Add(o, context);
return (context, true); return (context, true);
} }
@@ -98,14 +99,20 @@ internal class SerializedObject
} }
else if (fieldType.IsArray) else if (fieldType.IsArray)
{ {
Log.Error($"Array serialization not yet implemented"); Array myArray = fieldValue as Array;
if (myArray?.Rank > 1)
{
throw new NotImplementedException("Serializing multidimensional arrays is not yet supported.");
}
SerializeList(fieldName, fieldValue, fieldType, fieldType.GetElementType());
} }
else if (fieldType.IsGenericType) else if (fieldType.IsGenericType)
{ {
if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) if (fieldType.GetGenericTypeDefinition() == typeof(List<>))
{ {
//SerializeList(fieldName, type, o); SerializeList(fieldName, fieldValue, fieldType, fieldType.GetGenericArguments()[0]);
Log.Error($"List serialization not yet implemented");
} }
//else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) //else if (fieldType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
//{ //{
@@ -131,6 +138,36 @@ internal class SerializedObject
} }
} }
private void SerializeList(string fieldName, object listObject, Type fieldType, Type elementType)
{
if (listObject == null)
{
AddField(fieldName, SerializationType.ObjectReference, UUID.Zero);
}
else
{
Debug.Assert(listObject is IList);
var (context, newContext) = GetSerializedObject(listObject);
if (newContext)
{
IList list = (IList)listObject;
context.AddField("Count", SerializationType.Int32, list.Count);
for (int i = 0; i < list.Count; i++)
{
object element = list[i];
context.SerializeField($"{i}", element, element?.GetType() ?? elementType);
}
}
AddField(fieldName, SerializationType.ObjectReference, context._id);
}
}
private void SerializePrimitive(string fieldName, object fieldValue, Type fieldType) private void SerializePrimitive(string fieldName, object fieldValue, Type fieldType)
{ {
Debug.Assert(fieldType.IsPrimitive, $"{fieldType} is not a primitive type."); Debug.Assert(fieldType.IsPrimitive, $"{fieldType} is not a primitive type.");