From 4d6a4da02acaa6b94fddeb3e212ea125ad550174 Mon Sep 17 00:00:00 2001 From: Curtis Wensley Date: Tue, 1 Aug 2023 13:25:39 -0700 Subject: [PATCH] Fix serializing objects with custom child types using DataObject/Clipboard --- src/Eto/Forms/ObjectData.cs | 33 +++++++++++++++++-- .../UnitTests/Forms/ClipboardTests.cs | 10 ++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Eto/Forms/ObjectData.cs b/src/Eto/Forms/ObjectData.cs index f751a42ac..a7fb1c9cf 100755 --- a/src/Eto/Forms/ObjectData.cs +++ b/src/Eto/Forms/ObjectData.cs @@ -26,7 +26,7 @@ internal static byte[] Serialize(object value, Type baseType) using (var ms = new MemoryStream()) { - var serializer = new DataContractSerializer(typeof(ObjectData), new [] { baseType }); + var serializer = new DataContractSerializer(typeof(ObjectData), GetKnownTypes(baseType)); serializer.WriteObject(ms, data); // for debugging @@ -35,6 +35,34 @@ internal static byte[] Serialize(object value, Type baseType) return ms.ToArray(); } } + + static IEnumerable GetKnownTypes(Type baseType) + { + yield return baseType; + + // DataContracts must specify their types explicitly using the KnownTypeAttribute + var isDataContract = baseType.GetCustomAttribute() != null; + if (isDataContract) + yield break; + + var knownTypes = new HashSet(); + + foreach (var field in FormatterServices.GetSerializableMembers(baseType)) + { + var type = field switch + { + PropertyInfo pi => pi.PropertyType, + FieldInfo fi => fi.FieldType, + _ => null + }; + if (!knownTypes.Contains(type)) + { + yield return type; + knownTypes.Add(type); + } + } + } + internal static object Deserialize(Stream stream, Type objectType) { @@ -73,7 +101,8 @@ internal static object Deserialize(Stream stream, Type objectType) { // read again, but with known type populated stream.Position = 0; - var serializer = new DataContractSerializer(typeof(ObjectData), new[] { objectType }); + + var serializer = new DataContractSerializer(typeof(ObjectData), GetKnownTypes(objectType)); var data = serializer.ReadObject(stream) as ObjectData; ms?.Dispose(); diff --git a/test/Eto.Test/UnitTests/Forms/ClipboardTests.cs b/test/Eto.Test/UnitTests/Forms/ClipboardTests.cs index 451c4e6e9..b76fd81f5 100755 --- a/test/Eto.Test/UnitTests/Forms/ClipboardTests.cs +++ b/test/Eto.Test/UnitTests/Forms/ClipboardTests.cs @@ -61,6 +61,7 @@ public class SerializableObject : ISerializable { public string SomeValue { get; set; } + public ChildObject ChildObject { get; set; } = new ChildObject(); public SerializableObject() { } @@ -68,11 +69,13 @@ public SerializableObject() public SerializableObject(SerializationInfo info, StreamingContext context) { SomeValue = info.GetString("SomeValue"); + ChildObject = info.GetValue("Child", typeof(ChildObject)) as ChildObject; } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("SomeValue", SomeValue); + info.AddValue("Child", ChildObject); } } @@ -80,6 +83,13 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) public class SomeOtherObject { public string SomeValue { get; set; } + + public ChildObject ChildObject { get; set; } = new ChildObject(); + } + + public class ChildObject + { + public bool SomeProperty { get; set; } = new Random().Next() % 2 == 0; } static byte[] SampleByteData => new byte[] { 10, 20, 30 };