Skip to content

Commit 22343b7

Browse files
authored
Add deserialization support for ReadOnlyDictionary (#177)
* Create spec for ReadOnlyDictionary deserializer * Add support for ReadOnlyDictionary serializer
1 parent 7afb21e commit 22343b7

File tree

2 files changed

+97
-14
lines changed

2 files changed

+97
-14
lines changed

src/Hyperion.Tests/ImmutableCollectionsTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
using System.Collections.Generic;
1111
using System.Collections.Immutable;
12+
using System.Collections.ObjectModel;
1213
using System.Linq;
1314
using Xunit;
1415

@@ -150,6 +151,62 @@ public void CanSerializeImmutableDictionary()
150151
Assert.Equal(expected.ToList(), actual.ToList());
151152
}
152153

154+
[Fact]
155+
public void CanSerializeIReadOnlyDictionary()
156+
{
157+
var dict = ImmutableDictionary.CreateRange(new Dictionary<string, Something>
158+
{
159+
["a1"] = new Something
160+
{
161+
BoolProp = true,
162+
Else = new Else
163+
{
164+
Name = "Yoho"
165+
},
166+
Int32Prop = 999,
167+
StringProp = "Yesbox!"
168+
},
169+
["a2"] = new Something(),
170+
["a3"] = new Something(),
171+
["a4"] = null
172+
});
173+
174+
var expected = (IReadOnlyDictionary<string, Something>)dict;
175+
176+
Serialize(expected);
177+
Reset();
178+
var actual = Deserialize<IReadOnlyDictionary<string, Something>>();
179+
Assert.Equal(expected.ToList(), actual.ToList());
180+
}
181+
182+
[Fact]
183+
public void CanSerializeReadOnlyDictionary()
184+
{
185+
var dict = new Dictionary<string, Something>
186+
{
187+
["a1"] = new Something
188+
{
189+
BoolProp = true,
190+
Else = new Else
191+
{
192+
Name = "Yoho"
193+
},
194+
Int32Prop = 999,
195+
StringProp = "Yesbox!"
196+
},
197+
["a2"] = new Something(),
198+
["a3"] = new Something(),
199+
["a4"] = null
200+
};
201+
202+
var expected = new ReadOnlyDictionary<string, Something>(dict);
203+
204+
Serialize(expected);
205+
Reset();
206+
var actual = Deserialize<ReadOnlyDictionary<string, Something>>();
207+
Assert.Equal(expected.ToList(), actual.ToList());
208+
}
209+
153210
[Fact]
154211
public void CanSerializeImmutableQueue()
155212
{

src/Hyperion/SerializerFactories/DictionarySerializerFactory.cs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Collections;
1212
using System.Collections.Concurrent;
1313
using System.Collections.Generic;
14+
using System.Collections.ObjectModel;
1415
using System.Linq;
1516
using System.Reflection;
1617
using Hyperion.Extensions;
@@ -45,6 +46,44 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type
4546
ObjectReader reader = (stream, session) =>
4647
{
4748
object instance;
49+
50+
void ReadDictionaryKeyValuePairs(object dictionaryInstance)
51+
{
52+
var count = stream.ReadInt32(session);
53+
for (var i = 0; i < count; i++)
54+
{
55+
var entry = stream.ReadObject(session); // KeyValuePair<TKey, TValue>
56+
57+
// Get entry.Key and entry.Value
58+
var key = dictionaryTypes.KeyValuePairType.GetProperty(nameof(KeyValuePair<object, object>.Key)).GetValue(entry, null);
59+
var value = dictionaryTypes.KeyValuePairType.GetProperty(nameof(KeyValuePair<object, object>.Value)).GetValue(entry, null);
60+
61+
// Same as: instance.Add(key, value)
62+
dictionaryTypes.DictionaryInterfaceType
63+
.GetMethod(nameof(IDictionary<object, object>.Add), new[] { dictionaryTypes.KeyType, dictionaryTypes.ValueType })
64+
.Invoke(dictionaryInstance, new[] { key, value });
65+
}
66+
}
67+
68+
#region Special case for ReadOnlyDictionary
69+
// Special case for ReadOnlyDictionary since ReadOnlyDictionary
70+
// does not have a parameterless constructor
71+
var genericReadOnlyDictionary = typeof(ReadOnlyDictionary<,>);
72+
var readOnlyDictionaryType =
73+
genericReadOnlyDictionary.MakeGenericType(dictionaryTypes.KeyType, dictionaryTypes.ValueType);
74+
if (type.Equals(readOnlyDictionaryType))
75+
{
76+
var genericDictionary = typeof(Dictionary<,>);
77+
var genericDictionaryType = genericDictionary.MakeGenericType(dictionaryTypes.KeyType, dictionaryTypes.ValueType);
78+
var dictionary = Activator.CreateInstance(genericDictionaryType); // normal dictionary
79+
80+
ReadDictionaryKeyValuePairs(dictionary);
81+
instance = Activator.CreateInstance(type, dictionary); // IDictionary<TKey, TValue>
82+
if (preserveObjectReferences) session.TrackDeserializedObject(instance);
83+
return instance;
84+
}
85+
#endregion
86+
4887
try
4988
{
5089
instance = Activator.CreateInstance(type, true); // IDictionary<TKey, TValue>
@@ -56,20 +95,7 @@ public override ValueSerializer BuildSerializer(Serializer serializer, Type type
5695
{
5796
session.TrackDeserializedObject(instance);
5897
}
59-
var count = stream.ReadInt32(session);
60-
for (var i = 0; i < count; i++)
61-
{
62-
var entry = stream.ReadObject(session); // KeyValuePair<TKey, TValue>
63-
64-
// Get entry.Key and entry.Value
65-
var key = dictionaryTypes.KeyValuePairType.GetProperty(nameof(KeyValuePair<object, object>.Key)).GetValue(entry, null);
66-
var value = dictionaryTypes.KeyValuePairType.GetProperty(nameof(KeyValuePair<object, object>.Value)).GetValue(entry, null);
67-
68-
// Same as: instance.Add(key, value)
69-
dictionaryTypes.DictionaryInterfaceType
70-
.GetMethod(nameof(IDictionary<object, object>.Add), new []{ dictionaryTypes.KeyType, dictionaryTypes.ValueType })
71-
.Invoke(instance, new [] { key, value });
72-
}
98+
ReadDictionaryKeyValuePairs(instance);
7399

74100
return instance;
75101
};

0 commit comments

Comments
 (0)