EnumerableSerializerBase.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /* Copyright 2010-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;
  17. using System.Collections.Generic;
  18. using MongoDB.Bson.Serialization.Conventions;
  19. namespace MongoDB.Bson.Serialization.Serializers
  20. {
  21. /// <summary>
  22. /// Represents a base serializer for enumerable values.
  23. /// </summary>
  24. /// <typeparam name="TValue">The type of the value.</typeparam>
  25. public abstract class EnumerableSerializerBase<TValue> : SerializerBase<TValue>, IBsonArraySerializer where TValue : class, IEnumerable
  26. {
  27. // private fields
  28. private readonly IDiscriminatorConvention _discriminatorConvention = new ScalarDiscriminatorConvention("_t");
  29. private readonly Lazy<IBsonSerializer> _lazyItemSerializer;
  30. // constructors
  31. /// <summary>
  32. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue}"/> class.
  33. /// </summary>
  34. protected EnumerableSerializerBase()
  35. : this(BsonSerializer.SerializerRegistry)
  36. {
  37. }
  38. /// <summary>
  39. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue}"/> class.
  40. /// </summary>
  41. /// <param name="itemSerializer">The item serializer.</param>
  42. protected EnumerableSerializerBase(IBsonSerializer itemSerializer)
  43. {
  44. if (itemSerializer == null)
  45. {
  46. throw new ArgumentNullException("itemSerializer");
  47. }
  48. _lazyItemSerializer = new Lazy<IBsonSerializer>(() => itemSerializer);
  49. }
  50. /// <summary>
  51. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue}" /> class.
  52. /// </summary>
  53. /// <param name="serializerRegistry">The serializer registry.</param>
  54. protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry)
  55. {
  56. if (serializerRegistry == null)
  57. {
  58. throw new ArgumentNullException("serializerRegistry");
  59. }
  60. _lazyItemSerializer = new Lazy<IBsonSerializer>(() => serializerRegistry.GetSerializer(typeof(object)));
  61. }
  62. /// <summary>
  63. /// Gets the item serializer.
  64. /// </summary>
  65. /// <value>
  66. /// The item serializer.
  67. /// </value>
  68. public IBsonSerializer ItemSerializer
  69. {
  70. get { return _lazyItemSerializer.Value; }
  71. }
  72. // public methods
  73. /// <summary>
  74. /// Deserializes a value.
  75. /// </summary>
  76. /// <param name="context">The deserialization context.</param>
  77. /// <param name="args">The deserialization args.</param>
  78. /// <returns>A deserialized value.</returns>
  79. public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
  80. {
  81. var bsonReader = context.Reader;
  82. var bsonType = bsonReader.GetCurrentBsonType();
  83. switch (bsonType)
  84. {
  85. case BsonType.Null:
  86. bsonReader.ReadNull();
  87. return null;
  88. case BsonType.Array:
  89. bsonReader.ReadStartArray();
  90. var accumulator = CreateAccumulator();
  91. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  92. {
  93. var item = _lazyItemSerializer.Value.Deserialize(context);
  94. AddItem(accumulator, item);
  95. }
  96. bsonReader.ReadEndArray();
  97. return FinalizeResult(accumulator);
  98. case BsonType.Document:
  99. var serializer = new DiscriminatedWrapperSerializer<TValue>(_discriminatorConvention, this);
  100. if (serializer.IsPositionedAtDiscriminatedWrapper(context))
  101. {
  102. return (TValue)serializer.Deserialize(context);
  103. }
  104. else
  105. {
  106. goto default;
  107. }
  108. default:
  109. throw CreateCannotDeserializeFromBsonTypeException(bsonType);
  110. }
  111. }
  112. /// <summary>
  113. /// Tries to get the serialization info for the individual items of the array.
  114. /// </summary>
  115. /// <param name="serializationInfo">The serialization information.</param>
  116. /// <returns>
  117. /// <c>true</c> if the serialization info exists; otherwise <c>false</c>.
  118. /// </returns>
  119. public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
  120. {
  121. var itemSerializer = _lazyItemSerializer.Value;
  122. serializationInfo = new BsonSerializationInfo(null, itemSerializer, itemSerializer.ValueType);
  123. return true;
  124. }
  125. /// <summary>
  126. /// Serializes a value.
  127. /// </summary>
  128. /// <param name="context">The serialization context.</param>
  129. /// <param name="args">The serialization args.</param>
  130. /// <param name="value">The object.</param>
  131. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
  132. {
  133. var bsonWriter = context.Writer;
  134. if (value == null)
  135. {
  136. bsonWriter.WriteNull();
  137. }
  138. else
  139. {
  140. var actualType = value.GetType();
  141. if (actualType == args.NominalType || args.SerializeAsNominalType)
  142. {
  143. bsonWriter.WriteStartArray();
  144. foreach (var item in EnumerateItemsInSerializationOrder(value))
  145. {
  146. _lazyItemSerializer.Value.Serialize(context, item);
  147. }
  148. bsonWriter.WriteEndArray();
  149. }
  150. else
  151. {
  152. var serializer = new DiscriminatedWrapperSerializer<TValue>(_discriminatorConvention, this);
  153. serializer.Serialize(context, value);
  154. }
  155. }
  156. }
  157. // protected methods
  158. /// <summary>
  159. /// Adds the item.
  160. /// </summary>
  161. /// <param name="accumulator">The accumulator.</param>
  162. /// <param name="item">The item.</param>
  163. protected abstract void AddItem(object accumulator, object item);
  164. /// <summary>
  165. /// Creates the accumulator.
  166. /// </summary>
  167. /// <returns>The accumulator.</returns>
  168. protected abstract object CreateAccumulator();
  169. /// <summary>
  170. /// Enumerates the items in serialization order.
  171. /// </summary>
  172. /// <param name="value">The value.</param>
  173. /// <returns>The items.</returns>
  174. protected abstract IEnumerable EnumerateItemsInSerializationOrder(TValue value);
  175. /// <summary>
  176. /// Finalizes the result.
  177. /// </summary>
  178. /// <param name="accumulator">The accumulator.</param>
  179. /// <returns>The final result.</returns>
  180. protected abstract TValue FinalizeResult(object accumulator);
  181. }
  182. /// <summary>
  183. /// Represents a serializer for enumerable values.
  184. /// </summary>
  185. /// <typeparam name="TValue">The type of the value.</typeparam>
  186. /// <typeparam name="TItem">The type of the items.</typeparam>
  187. public abstract class EnumerableSerializerBase<TValue, TItem> : SerializerBase<TValue>, IBsonArraySerializer where TValue : class, IEnumerable<TItem>
  188. {
  189. // private fields
  190. private readonly IDiscriminatorConvention _discriminatorConvention = new ScalarDiscriminatorConvention("_t");
  191. private readonly Lazy<IBsonSerializer<TItem>> _lazyItemSerializer;
  192. // constructors
  193. /// <summary>
  194. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue, TItem}"/> class.
  195. /// </summary>
  196. protected EnumerableSerializerBase()
  197. : this(BsonSerializer.SerializerRegistry)
  198. {
  199. }
  200. /// <summary>
  201. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue, TItem}"/> class.
  202. /// </summary>
  203. /// <param name="itemSerializer">The item serializer.</param>
  204. protected EnumerableSerializerBase(IBsonSerializer<TItem> itemSerializer)
  205. {
  206. if (itemSerializer == null)
  207. {
  208. throw new ArgumentNullException("itemSerializer");
  209. }
  210. _lazyItemSerializer = new Lazy<IBsonSerializer<TItem>>(() => itemSerializer);
  211. }
  212. /// <summary>
  213. /// Initializes a new instance of the <see cref="EnumerableSerializerBase{TValue, TItem}" /> class.
  214. /// </summary>
  215. /// <param name="serializerRegistry">The serializer registry.</param>
  216. protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry)
  217. {
  218. if (serializerRegistry == null)
  219. {
  220. throw new ArgumentNullException("serializerRegistry");
  221. }
  222. _lazyItemSerializer = new Lazy<IBsonSerializer<TItem>>(() => serializerRegistry.GetSerializer<TItem>());
  223. }
  224. // public properties
  225. /// <summary>
  226. /// Gets the item serializer.
  227. /// </summary>
  228. /// <value>
  229. /// The item serializer.
  230. /// </value>
  231. public IBsonSerializer<TItem> ItemSerializer
  232. {
  233. get { return _lazyItemSerializer.Value; }
  234. }
  235. // public methods
  236. /// <summary>
  237. /// Deserializes a value.
  238. /// </summary>
  239. /// <param name="context">The deserialization context.</param>
  240. /// <param name="args">The deserialization args.</param>
  241. /// <returns>A deserialized value.</returns>
  242. public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
  243. {
  244. var bsonReader = context.Reader;
  245. var bsonType = bsonReader.GetCurrentBsonType();
  246. switch (bsonType)
  247. {
  248. case BsonType.Null:
  249. bsonReader.ReadNull();
  250. return null;
  251. case BsonType.Array:
  252. bsonReader.ReadStartArray();
  253. var accumulator = CreateAccumulator();
  254. while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
  255. {
  256. var item = _lazyItemSerializer.Value.Deserialize(context);
  257. AddItem(accumulator, item);
  258. }
  259. bsonReader.ReadEndArray();
  260. return FinalizeResult(accumulator);
  261. case BsonType.Document:
  262. var serializer = new DiscriminatedWrapperSerializer<TValue>(_discriminatorConvention, this);
  263. if (serializer.IsPositionedAtDiscriminatedWrapper(context))
  264. {
  265. return (TValue)serializer.Deserialize(context);
  266. }
  267. else
  268. {
  269. goto default;
  270. }
  271. default:
  272. throw CreateCannotDeserializeFromBsonTypeException(bsonType);
  273. }
  274. }
  275. /// <summary>
  276. /// Tries to get the serialization info for the individual items of the array.
  277. /// </summary>
  278. /// <param name="serializationInfo">The serialization information.</param>
  279. /// <returns>
  280. /// The serialization info for the items.
  281. /// </returns>
  282. public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
  283. {
  284. var serializer = _lazyItemSerializer.Value;
  285. serializationInfo = new BsonSerializationInfo(null, serializer, serializer.ValueType);
  286. return true;
  287. }
  288. /// <summary>
  289. /// Serializes a value.
  290. /// </summary>
  291. /// <param name="context">The serialization context.</param>
  292. /// <param name="args">The serialization args.</param>
  293. /// <param name="value">The object.</param>
  294. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
  295. {
  296. var bsonWriter = context.Writer;
  297. if (value == null)
  298. {
  299. bsonWriter.WriteNull();
  300. }
  301. else
  302. {
  303. var actualType = value.GetType();
  304. if (actualType == args.NominalType)
  305. {
  306. bsonWriter.WriteStartArray();
  307. foreach (var item in EnumerateItemsInSerializationOrder(value))
  308. {
  309. _lazyItemSerializer.Value.Serialize(context, item);
  310. }
  311. bsonWriter.WriteEndArray();
  312. }
  313. else
  314. {
  315. var serializer = new DiscriminatedWrapperSerializer<TValue>(_discriminatorConvention, this);
  316. serializer.Serialize(context, value);
  317. }
  318. }
  319. }
  320. // protected methods
  321. /// <summary>
  322. /// Adds the item.
  323. /// </summary>
  324. /// <param name="accumulator">The accumulator.</param>
  325. /// <param name="item">The item.</param>
  326. protected abstract void AddItem(object accumulator, TItem item);
  327. /// <summary>
  328. /// Creates the accumulator.
  329. /// </summary>
  330. /// <returns>The accumulator.</returns>
  331. protected abstract object CreateAccumulator();
  332. /// <summary>
  333. /// Enumerates the items in serialization order.
  334. /// </summary>
  335. /// <param name="value">The value.</param>
  336. /// <returns>The items.</returns>
  337. protected abstract IEnumerable<TItem> EnumerateItemsInSerializationOrder(TValue value);
  338. /// <summary>
  339. /// Finalizes the result.
  340. /// </summary>
  341. /// <param name="accumulator">The accumulator.</param>
  342. /// <returns>The result.</returns>
  343. protected abstract TValue FinalizeResult(object accumulator);
  344. }
  345. }