FieldValueSerializerHelper.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* Copyright 2016-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.ComponentModel;
  18. using System.Reflection;
  19. using MongoDB.Bson;
  20. using MongoDB.Bson.Serialization;
  21. using MongoDB.Bson.Serialization.Serializers;
  22. using MongoDB.Driver.Support;
  23. namespace MongoDB.Driver
  24. {
  25. internal static class FieldValueSerializerHelper
  26. {
  27. public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSerializer, IBsonSerializerRegistry serializerRegistry, Type valueType)
  28. {
  29. return GetSerializerForValueType(fieldSerializer, serializerRegistry, valueType, 0);
  30. }
  31. private static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSerializer, IBsonSerializerRegistry serializerRegistry, Type valueType, int recursionLevel)
  32. {
  33. if (recursionLevel > 1)
  34. {
  35. throw new ArgumentException("Unexpectedly high recursion level.", nameof(recursionLevel));
  36. }
  37. var fieldType = fieldSerializer.ValueType;
  38. // these will normally be equal unless we've removed some Convert(s) that the compiler put in
  39. if (fieldType == valueType)
  40. {
  41. return fieldSerializer;
  42. }
  43. // serialize numeric values without converting them
  44. if (fieldType.IsNumeric() && valueType.IsNumeric())
  45. {
  46. var valueSerializer = BsonSerializer.SerializerRegistry.GetSerializer(valueType);
  47. if (HasStringRepresentation(fieldSerializer))
  48. {
  49. valueSerializer = WithStringRepresentation(valueSerializer);
  50. }
  51. return valueSerializer;
  52. }
  53. var fieldTypeInfo = fieldType.GetTypeInfo();
  54. var fieldSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(fieldType);
  55. var valueTypeInfo = valueType.GetTypeInfo();
  56. // synthesize a NullableSerializer using the field serializer
  57. if (valueType.IsNullable() && valueType.GetNullableUnderlyingType() == fieldType)
  58. {
  59. var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(fieldType);
  60. var nullableSerializerConstructor = nullableSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
  61. return (IBsonSerializer)nullableSerializerConstructor.Invoke(new object[] { fieldSerializer });
  62. }
  63. // synthesize an EnumConvertingSerializer using the field serializer
  64. if (fieldTypeInfo.IsEnum)
  65. {
  66. if (valueType.IsConvertibleToEnum())
  67. {
  68. var enumConvertingSerializerType = typeof(EnumConvertingSerializer<,>).MakeGenericType(valueType, fieldType);
  69. var enumConvertingSerializerConstructor = enumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
  70. return (IBsonSerializer)enumConvertingSerializerConstructor.Invoke(new object[] { fieldSerializer });
  71. }
  72. if (valueType.IsNullable() && valueType.GetNullableUnderlyingType().IsConvertibleToEnum())
  73. {
  74. var underlyingValueType = valueType.GetNullableUnderlyingType();
  75. var underlyingValueSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(underlyingValueType);
  76. var enumConvertingSerializerType = typeof(EnumConvertingSerializer<,>).MakeGenericType(underlyingValueType, fieldType);
  77. var enumConvertingSerializerConstructor = enumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType });
  78. var enumConvertingSerializer = enumConvertingSerializerConstructor.Invoke(new object[] { fieldSerializer });
  79. var nullableSerializerType = typeof(NullableSerializer<>).MakeGenericType(underlyingValueType);
  80. var nullableSerializerConstructor = nullableSerializerType.GetTypeInfo().GetConstructor(new[] { underlyingValueSerializerInterfaceType });
  81. return (IBsonSerializer)nullableSerializerConstructor.Invoke(new object[] { enumConvertingSerializer });
  82. }
  83. }
  84. // synthesize a NullableEnumConvertingSerializer using the field serializer
  85. if (fieldType.IsNullableEnum() && valueType.IsNullable())
  86. {
  87. var nonNullableFieldType = fieldType.GetNullableUnderlyingType();
  88. var nonNullableValueType = valueType.GetNullableUnderlyingType();
  89. var nonNullableFieldSerializer = ((IChildSerializerConfigurable)fieldSerializer).ChildSerializer;
  90. var nonNullableFieldSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(nonNullableFieldType);
  91. var nullableEnumConvertingSerializerType = typeof(NullableEnumConvertingSerializer<,>).MakeGenericType(nonNullableValueType, nonNullableFieldType);
  92. var nullableEnumConvertingSerializerConstructor = nullableEnumConvertingSerializerType.GetTypeInfo().GetConstructor(new[] { nonNullableFieldSerializerInterfaceType });
  93. return (IBsonSerializer)nullableEnumConvertingSerializerConstructor.Invoke(new object[] { nonNullableFieldSerializer });
  94. }
  95. // synthesize an IEnumerableSerializer serializer using the item serializer from the field serializer
  96. Type fieldIEnumerableInterfaceType;
  97. Type valueIEnumerableInterfaceType;
  98. Type itemType;
  99. if (
  100. (fieldIEnumerableInterfaceType = fieldType.FindIEnumerable()) != null &&
  101. (valueIEnumerableInterfaceType = valueType.FindIEnumerable()) != null &&
  102. (itemType = fieldIEnumerableInterfaceType.GetSequenceElementType()) == valueIEnumerableInterfaceType.GetSequenceElementType() &&
  103. fieldSerializer is IChildSerializerConfigurable)
  104. {
  105. var itemSerializer = ((IChildSerializerConfigurable)fieldSerializer).ChildSerializer;
  106. var itemSerializerInterfaceType = typeof(IBsonSerializer<>).MakeGenericType(itemType);
  107. var ienumerableSerializerType = typeof(IEnumerableSerializer<>).MakeGenericType(itemType);
  108. var ienumerableSerializerConstructor = ienumerableSerializerType.GetTypeInfo().GetConstructor(new[] { itemSerializerInterfaceType });
  109. return (IBsonSerializer)ienumerableSerializerConstructor.Invoke(new object[] { itemSerializer });
  110. }
  111. // if the fieldSerializer is an array serializer try to adapt its itemSerializer for valueType
  112. IBsonArraySerializer arraySerializer;
  113. if ((arraySerializer = fieldSerializer as IBsonArraySerializer) != null)
  114. {
  115. BsonSerializationInfo itemSerializationInfo;
  116. if (arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo))
  117. {
  118. if (recursionLevel == 0)
  119. {
  120. var itemSerializer = itemSerializationInfo.Serializer;
  121. return GetSerializerForValueType(itemSerializer, serializerRegistry, valueType, recursionLevel + 1);
  122. }
  123. }
  124. }
  125. // if we can't return a value serializer based on the field serializer return a converting serializer
  126. var convertIfPossibleSerializerType = typeof(ConvertIfPossibleSerializer<,>).MakeGenericType(valueType, fieldType);
  127. var convertIfPossibleSerializerConstructor = convertIfPossibleSerializerType.GetTypeInfo().GetConstructor(new[] { fieldSerializerInterfaceType, typeof(IBsonSerializerRegistry) });
  128. return (IBsonSerializer)convertIfPossibleSerializerConstructor.Invoke(new object[] { fieldSerializer, serializerRegistry });
  129. }
  130. public static IBsonSerializer GetSerializerForValueType(IBsonSerializer fieldSerializer, IBsonSerializerRegistry serializerRegistry, Type valueType, object value)
  131. {
  132. if (!valueType.GetTypeInfo().IsValueType && value == null)
  133. {
  134. return fieldSerializer;
  135. }
  136. else
  137. {
  138. return GetSerializerForValueType(fieldSerializer, serializerRegistry, valueType);
  139. }
  140. }
  141. // private static methods
  142. private static bool HasStringRepresentation(IBsonSerializer serializer)
  143. {
  144. var configurableSerializer = serializer as IRepresentationConfigurable;
  145. if (configurableSerializer != null)
  146. {
  147. return configurableSerializer.Representation == BsonType.String;
  148. }
  149. else
  150. {
  151. return false;
  152. }
  153. }
  154. private static IBsonSerializer WithStringRepresentation(IBsonSerializer serializer)
  155. {
  156. var configurableSerializer = serializer as IRepresentationConfigurable;
  157. if (configurableSerializer != null)
  158. {
  159. return configurableSerializer.WithRepresentation(BsonType.String);
  160. }
  161. else
  162. {
  163. return serializer;
  164. }
  165. }
  166. // nested types
  167. private class ConvertIfPossibleSerializer<TFrom, TTo> : SerializerBase<TFrom>
  168. {
  169. private readonly IBsonSerializer<TTo> _serializer;
  170. private readonly IBsonSerializerRegistry _serializerRegistry;
  171. public ConvertIfPossibleSerializer(IBsonSerializer<TTo> serializer, IBsonSerializerRegistry serializerRegistry)
  172. {
  173. _serializer = serializer;
  174. _serializerRegistry = serializerRegistry;
  175. }
  176. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TFrom value)
  177. {
  178. TTo convertedValue;
  179. if (TryConvertValue(value, out convertedValue))
  180. {
  181. args.NominalType = typeof(TTo);
  182. _serializer.Serialize(context, args, convertedValue);
  183. }
  184. else
  185. {
  186. var serializer = _serializerRegistry.GetSerializer<TFrom>();
  187. serializer.Serialize(context, args, value);
  188. }
  189. }
  190. private bool TryConvertValue(TFrom value, out TTo convertedValue)
  191. {
  192. if (object.ReferenceEquals(value, null))
  193. {
  194. convertedValue = (TTo)(object)null;
  195. return true;
  196. }
  197. Type fromType = value.GetType();
  198. Type toType = typeof(TTo);
  199. if (toType.GetTypeInfo().IsAssignableFrom(fromType))
  200. {
  201. convertedValue = (TTo)(object)value;
  202. return true;
  203. }
  204. var toConverter = TypeDescriptor.GetConverter(toType);
  205. if (toConverter.CanConvertFrom(fromType))
  206. {
  207. convertedValue = (TTo)toConverter.ConvertFrom(value);
  208. return true;
  209. }
  210. var fromConverter = TypeDescriptor.GetConverter(fromType);
  211. if (fromConverter.CanConvertTo(toType))
  212. {
  213. convertedValue = (TTo)fromConverter.ConvertTo(value, toType);
  214. return true;
  215. }
  216. try
  217. {
  218. convertedValue = (TTo)Convert.ChangeType(value, toType);
  219. return true;
  220. }
  221. catch { }
  222. convertedValue = default(TTo);
  223. return false;
  224. }
  225. }
  226. private class EnumConvertingSerializer<TFrom, TTo> : SerializerBase<TFrom>
  227. {
  228. private readonly IBsonSerializer<TTo> _serializer;
  229. public EnumConvertingSerializer(IBsonSerializer<TTo> serializer)
  230. {
  231. _serializer = serializer;
  232. }
  233. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TFrom value)
  234. {
  235. TTo convertedValue;
  236. if (typeof(TFrom) == typeof(string))
  237. {
  238. convertedValue = (TTo)Enum.Parse(typeof(TTo), (string)(object)value);
  239. }
  240. else
  241. {
  242. convertedValue = (TTo)Enum.ToObject(typeof(TTo), (object)value);
  243. }
  244. _serializer.Serialize(context, args, convertedValue);
  245. }
  246. }
  247. private class IEnumerableSerializer<TItem> : SerializerBase<IEnumerable<TItem>>
  248. {
  249. private readonly IBsonSerializer<TItem> _itemSerializer;
  250. public IEnumerableSerializer(IBsonSerializer<TItem> itemSerializer)
  251. {
  252. _itemSerializer = itemSerializer;
  253. }
  254. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, IEnumerable<TItem> value)
  255. {
  256. var bsonWriter = context.Writer;
  257. if (value == null)
  258. {
  259. bsonWriter.WriteNull();
  260. }
  261. else
  262. {
  263. bsonWriter.WriteStartArray();
  264. foreach (var item in value)
  265. {
  266. _itemSerializer.Serialize(context, item);
  267. }
  268. bsonWriter.WriteEndArray();
  269. }
  270. }
  271. }
  272. private class NullableEnumConvertingSerializer<TFrom, TTo> : SerializerBase<Nullable<TFrom>> where TFrom : struct where TTo : struct
  273. {
  274. private readonly IBsonSerializer<TTo> _nonNullableEnumSerializer;
  275. public NullableEnumConvertingSerializer(IBsonSerializer<TTo> nonNullableEnumSerializer)
  276. {
  277. _nonNullableEnumSerializer = nonNullableEnumSerializer;
  278. }
  279. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Nullable<TFrom> value)
  280. {
  281. if (value == null)
  282. {
  283. context.Writer.WriteNull();
  284. }
  285. else
  286. {
  287. _nonNullableEnumSerializer.Serialize(context, args, (TTo)Enum.ToObject(typeof(TTo), (object)value.Value));
  288. }
  289. }
  290. }
  291. }
  292. }