Skip to content

Commit 88c5eed

Browse files
rstamcraiggwilson
authored andcommitted
Implemented CSHARP-460. Classes that implement collection interfaces are now serialized using either an EnumerableSerializer or a DictionarySerializer. Also added a KeyValuePairSerializer. Thanks to Alex Nagy for providing a prototype that a good bit of this is based on.
1 parent 1264c9e commit 88c5eed

16 files changed

+1027
-381
lines changed

Bson/Bson.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
<Compile Include="Serialization\Options\DateTimeSerializationOptions.cs" />
113113
<Compile Include="Serialization\Options\DictionarySerializationOptions.cs" />
114114
<Compile Include="Serialization\Options\DocumentSerializationOptions.cs" />
115+
<Compile Include="Serialization\Options\KeyValuePairSerializationOptions.cs" />
115116
<Compile Include="Serialization\Options\RepresentationSerializationOptions.cs" />
116117
<Compile Include="Exceptions\TruncationException.cs" />
117118
<Compile Include="IO\BsonDocumentReader.cs" />
@@ -145,6 +146,7 @@
145146
<Compile Include="Serialization\BsonSerializationInfo.cs" />
146147
<Compile Include="Serialization\Serializers\CollectionGenericSerializers.cs" />
147148
<Compile Include="Serialization\Serializers\CollectionSerializers.cs" />
149+
<Compile Include="Serialization\Serializers\KeyValuePairSerializer.cs" />
148150
<Compile Include="Serialization\Serializers\DictionaryGenericSerializer.cs" />
149151
<Compile Include="Serialization\Serializers\DictionarySerializer.cs" />
150152
<Compile Include="Serialization\Serializers\ImageSerializers.cs" />

Bson/Serialization/BsonClassMapSerializer.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,15 +275,6 @@ public bool GetDocumentId(
275275
}
276276
}
277277

278-
/// <summary>
279-
/// Gets the serialization info for individual items of an enumerable type.
280-
/// </summary>
281-
/// <returns>The serialization info for the items.</returns>
282-
public BsonSerializationInfo GetItemSerializationInfo()
283-
{
284-
throw new NotSupportedException("BsonClassMapSerializer does not implement the GetItemSerializationInfo method.");
285-
}
286-
287278
/// <summary>
288279
/// Gets the serialization info for a member.
289280
/// </summary>

Bson/Serialization/BsonDefaultSerializationProvider.cs

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ static BsonDefaultSerializationProvider()
4141
{
4242
__serializers = new Dictionary<Type, IBsonSerializer>
4343
{
44-
{ typeof(ArrayList), EnumerableSerializer.Instance },
4544
{ typeof(BitArray), BitArraySerializer.Instance },
4645
{ typeof(Bitmap), BitmapSerializer.Instance },
4746
{ typeof(Boolean), BooleanSerializer.Instance },
@@ -76,26 +75,18 @@ static BsonDefaultSerializationProvider()
7675
{ typeof(Double), DoubleSerializer.Instance },
7776
{ typeof(System.Drawing.Size), DrawingSizeSerializer.Instance },
7877
{ typeof(Guid), GuidSerializer.Instance },
79-
{ typeof(Hashtable), DictionarySerializer.Instance },
8078
{ typeof(IBsonSerializable), BsonIBsonSerializableSerializer.Instance },
81-
{ typeof(ICollection), EnumerableSerializer.Instance },
82-
{ typeof(IDictionary), DictionarySerializer.Instance },
83-
{ typeof(IEnumerable), EnumerableSerializer.Instance },
84-
{ typeof(IList), EnumerableSerializer.Instance },
8579
{ typeof(Image), ImageSerializer.Instance },
8680
{ typeof(Int16), Int16Serializer.Instance },
8781
{ typeof(Int32), Int32Serializer.Instance },
8882
{ typeof(Int64), Int64Serializer.Instance },
8983
{ typeof(IPAddress), IPAddressSerializer.Instance },
9084
{ typeof(IPEndPoint), IPEndPointSerializer.Instance },
91-
{ typeof(ListDictionary), DictionarySerializer.Instance },
9285
{ typeof(Object), ObjectSerializer.Instance },
9386
{ typeof(ObjectId), ObjectIdSerializer.Instance },
94-
{ typeof(OrderedDictionary), DictionarySerializer.Instance },
9587
{ typeof(Queue), QueueSerializer.Instance },
9688
{ typeof(SByte), SByteSerializer.Instance },
9789
{ typeof(Single), SingleSerializer.Instance },
98-
{ typeof(SortedList), DictionarySerializer.Instance },
9990
{ typeof(Stack), StackSerializer.Instance },
10091
{ typeof(String), StringSerializer.Instance },
10192
{ typeof(TimeSpan), TimeSpanSerializer.Instance },
@@ -108,20 +99,9 @@ static BsonDefaultSerializationProvider()
10899

109100
__genericSerializerDefinitions = new Dictionary<Type, Type>
110101
{
111-
{ typeof(Collection<>), typeof(EnumerableSerializer<>)},
112-
{ typeof(Dictionary<,>), typeof(DictionarySerializer<,>) },
113-
{ typeof(HashSet<>), typeof(EnumerableSerializer<>) },
114-
{ typeof(ICollection<>), typeof(EnumerableSerializer<>) },
115-
{ typeof(IDictionary<,>), typeof(DictionarySerializer<,>) },
116-
{ typeof(IEnumerable<>), typeof(EnumerableSerializer<>) },
117-
{ typeof(IList<>), typeof(EnumerableSerializer<>) },
118-
{ typeof(LinkedList<>), typeof(EnumerableSerializer<>) },
119-
{ typeof(List<>), typeof(EnumerableSerializer<>) },
102+
{ typeof(KeyValuePair<,>), typeof(KeyValuePairSerializer<,>) },
120103
{ typeof(Nullable<>), typeof(NullableSerializer<>) },
121-
{ typeof(ObservableCollection<>), typeof(EnumerableSerializer<>)},
122104
{ typeof(Queue<>), typeof(QueueSerializer<>) },
123-
{ typeof(SortedDictionary<,>), typeof(DictionarySerializer<,>) },
124-
{ typeof(SortedList<,>), typeof(DictionarySerializer<,>) },
125105
{ typeof(Stack<>), typeof(StackSerializer<>) }
126106
};
127107
}
@@ -187,6 +167,82 @@ public IBsonSerializer GetSerializer(Type type)
187167
return EnumSerializer.Instance;
188168
}
189169

170+
// classes that implement IDictionary or IEnumerable are serialized using either DictionarySerializer or EnumerableSerializer
171+
// this does mean that any additional public properties the class might have won't be serialized (just like the XmlSerializer)
172+
var collectionSerializer = GetCollectionSerializer(type);
173+
if (collectionSerializer != null)
174+
{
175+
return collectionSerializer;
176+
}
177+
178+
return null;
179+
}
180+
181+
// private methods
182+
private IBsonSerializer GetCollectionSerializer(Type type)
183+
{
184+
Type implementedGenericDictionaryInterface = null;
185+
Type implementedGenericEnumerableInterface = null;
186+
Type implementedDictionaryInterface = null;
187+
Type implementedEnumerableInterface = null;
188+
189+
var implementedInterfaces = new List<Type>(type.GetInterfaces());
190+
if (type.IsInterface)
191+
{
192+
implementedInterfaces.Add(type);
193+
}
194+
foreach (var implementedInterface in implementedInterfaces)
195+
{
196+
if (implementedInterface.IsGenericType)
197+
{
198+
var genericInterfaceDefinition = implementedInterface.GetGenericTypeDefinition();
199+
if (genericInterfaceDefinition == typeof(IDictionary<,>))
200+
{
201+
implementedGenericDictionaryInterface = implementedInterface;
202+
}
203+
if (genericInterfaceDefinition == typeof(IEnumerable<>))
204+
{
205+
implementedGenericEnumerableInterface = implementedInterface;
206+
}
207+
}
208+
else
209+
{
210+
if (implementedInterface == typeof(IDictionary))
211+
{
212+
implementedDictionaryInterface = implementedInterface;
213+
}
214+
if (implementedInterface == typeof(IEnumerable))
215+
{
216+
implementedEnumerableInterface = implementedInterface;
217+
}
218+
}
219+
}
220+
221+
// the order of the tests is important
222+
if (implementedGenericDictionaryInterface != null)
223+
{
224+
var keyType = implementedGenericDictionaryInterface.GetGenericArguments()[0];
225+
var valueType = implementedGenericDictionaryInterface.GetGenericArguments()[1];
226+
var genericSerializerDefinition = typeof(DictionarySerializer<,>);
227+
var genericSerializerType = genericSerializerDefinition.MakeGenericType(keyType, valueType);
228+
return (IBsonSerializer)Activator.CreateInstance(genericSerializerType);
229+
}
230+
else if (implementedDictionaryInterface != null)
231+
{
232+
return DictionarySerializer.Instance;
233+
}
234+
else if (implementedGenericEnumerableInterface != null)
235+
{
236+
var valueType = implementedGenericEnumerableInterface.GetGenericArguments()[0];
237+
var genericSerializerDefinition = typeof(EnumerableSerializer<>);
238+
var genericSerializerType = genericSerializerDefinition.MakeGenericType(valueType);
239+
return (IBsonSerializer)Activator.CreateInstance(genericSerializerType);
240+
}
241+
else if (implementedEnumerableInterface != null)
242+
{
243+
return EnumerableSerializer.Instance;
244+
}
245+
190246
return null;
191247
}
192248
}

Bson/Serialization/Options/DictionarySerializationOptions.cs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public class DictionarySerializationOptions : BsonBaseSerializationOptions
5858

5959
// private fields
6060
private DictionaryRepresentation _representation = DictionaryRepresentation.Dynamic;
61-
private IBsonSerializationOptions _itemSerializationOptions;
61+
private IBsonSerializationOptions _valueSerializationOptions;
6262

6363
// constructors
6464
/// <summary>
@@ -81,11 +81,11 @@ public DictionarySerializationOptions(DictionaryRepresentation representation)
8181
/// Initializes a new instance of the DictionarySerializationOptions class.
8282
/// </summary>
8383
/// <param name="representation">The representation to use for a Dictionary.</param>
84-
/// <param name="itemSerializationOptions">The serialization options for the items in the dictionary.</param>
85-
public DictionarySerializationOptions(DictionaryRepresentation representation, IBsonSerializationOptions itemSerializationOptions)
84+
/// <param name="valueSerializationOptions">The serialization options for the values in the dictionary.</param>
85+
public DictionarySerializationOptions(DictionaryRepresentation representation, IBsonSerializationOptions valueSerializationOptions)
8686
{
8787
_representation = representation;
88-
_itemSerializationOptions = itemSerializationOptions;
88+
_valueSerializationOptions = valueSerializationOptions;
8989
}
9090

9191
// public static properties
@@ -131,6 +131,16 @@ public static DictionarySerializationOptions Dynamic
131131
}
132132

133133
// public properties
134+
/// <summary>
135+
/// Gets or sets the serialization options for the values in the dictionary.
136+
/// </summary>
137+
[Obsolete("Use ValueSerializationOptions instead.")]
138+
public IBsonSerializationOptions ItemSerializationOptions
139+
{
140+
get { return ValueSerializationOptions; }
141+
set { ValueSerializationOptions = value; }
142+
}
143+
134144
/// <summary>
135145
/// Gets the representation to use for a Dictionary.
136146
/// </summary>
@@ -145,15 +155,15 @@ public DictionaryRepresentation Representation
145155
}
146156

147157
/// <summary>
148-
/// Gets or sets the serialization options for the items in the dictionary.
158+
/// Gets or sets the serialization options for the values in the dictionary.
149159
/// </summary>
150-
public IBsonSerializationOptions ItemSerializationOptions
160+
public IBsonSerializationOptions ValueSerializationOptions
151161
{
152-
get { return _itemSerializationOptions; }
162+
get { return _valueSerializationOptions; }
153163
set
154164
{
155165
EnsureNotFrozen();
156-
_itemSerializationOptions = value;
166+
_valueSerializationOptions = value;
157167
}
158168
}
159169

@@ -173,7 +183,7 @@ public override void ApplyAttribute(IBsonSerializer serializer, Attribute attrib
173183
return;
174184
}
175185

176-
// for backward compatibility reasons representations Array and Document apply to the Dictionary and not the items
186+
// for backward compatibility reasons representations Array and Document apply to the Dictionary and not the values
177187
var representationAttribute = attribute as BsonRepresentationAttribute;
178188
if (representationAttribute != null)
179189
{
@@ -188,43 +198,39 @@ public override void ApplyAttribute(IBsonSerializer serializer, Attribute attrib
188198
}
189199
}
190200

191-
var itemSerializationInfoProvider = serializer as IBsonItemSerializationInfoProvider;
192-
if (itemSerializationInfoProvider == null)
201+
var valueType = typeof(object);
202+
if (serializer.GetType().IsGenericType)
193203
{
194-
var message = string.Format(
195-
"A serialization options attribute of type {0} cannot be used when the serializer is of type {1}.",
196-
BsonUtils.GetFriendlyTypeName(attribute.GetType()),
197-
BsonUtils.GetFriendlyTypeName(serializer.GetType()));
198-
throw new NotSupportedException(message);
204+
valueType = serializer.GetType().GetGenericArguments()[1];
199205
}
206+
var valueSerializer = BsonSerializer.LookupSerializer(valueType);
200207

201-
var itemSerializer = itemSerializationInfoProvider.GetItemSerializationInfo().Serializer;
202-
if (_itemSerializationOptions == null)
208+
if (_valueSerializationOptions == null)
203209
{
204-
var itemDefaultSerializationOptions = itemSerializer.GetDefaultSerializationOptions();
210+
var valueDefaultSerializationOptions = valueSerializer.GetDefaultSerializationOptions();
205211

206212
// special case for legacy dictionaries: allow BsonRepresentation on object
207-
if (itemDefaultSerializationOptions == null &&
213+
if (valueDefaultSerializationOptions == null &&
208214
serializer.GetType() == typeof(DictionarySerializer) &&
209215
attribute.GetType() == typeof(BsonRepresentationAttribute))
210216
{
211-
itemDefaultSerializationOptions = new RepresentationSerializationOptions(BsonType.Null); // will be modified later by ApplyAttribute
217+
valueDefaultSerializationOptions = new RepresentationSerializationOptions(BsonType.Null); // will be modified later by ApplyAttribute
212218
}
213219

214-
if (itemDefaultSerializationOptions == null)
220+
if (valueDefaultSerializationOptions == null)
215221
{
216222
var message = string.Format(
217-
"A serialization options attribute of type {0} cannot be used when the serializer is of type {1} and the item serializer is of type {2}.",
223+
"A serialization options attribute of type {0} cannot be used when the serializer is of type {1} and the value serializer is of type {2}.",
218224
BsonUtils.GetFriendlyTypeName(attribute.GetType()),
219225
BsonUtils.GetFriendlyTypeName(serializer.GetType()),
220-
BsonUtils.GetFriendlyTypeName(itemSerializer.GetType()));
226+
BsonUtils.GetFriendlyTypeName(valueSerializer.GetType()));
221227
throw new NotSupportedException(message);
222228
}
223229

224-
_itemSerializationOptions = itemDefaultSerializationOptions.Clone();
230+
_valueSerializationOptions = valueDefaultSerializationOptions.Clone();
225231
}
226232

227-
_itemSerializationOptions.ApplyAttribute(itemSerializer, attribute);
233+
_valueSerializationOptions.ApplyAttribute(valueSerializer, attribute);
228234
}
229235

230236
/// <summary>
@@ -233,7 +239,7 @@ public override void ApplyAttribute(IBsonSerializer serializer, Attribute attrib
233239
/// <returns>A cloned copy of the serialization options.</returns>
234240
public override IBsonSerializationOptions Clone()
235241
{
236-
return new DictionarySerializationOptions(_representation, _itemSerializationOptions);
242+
return new DictionarySerializationOptions(_representation, _valueSerializationOptions);
237243
}
238244

239245
/// <summary>
@@ -244,9 +250,9 @@ public override IBsonSerializationOptions Freeze()
244250
{
245251
if (!IsFrozen)
246252
{
247-
if (_itemSerializationOptions != null)
253+
if (_valueSerializationOptions != null)
248254
{
249-
_itemSerializationOptions.Freeze();
255+
_valueSerializationOptions.Freeze();
250256
}
251257
}
252258
return base.Freeze();

0 commit comments

Comments
 (0)