BsonSerializer.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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.Concurrent;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Linq;
  20. using System.Reflection;
  21. using System.Threading;
  22. // don't add using statement for MongoDB.Bson.Serialization.Serializers to minimize dependencies on DefaultSerializer
  23. using MongoDB.Bson.IO;
  24. using MongoDB.Bson.Serialization.Attributes;
  25. using MongoDB.Bson.Serialization.Conventions;
  26. using MongoDB.Bson.Serialization.IdGenerators;
  27. namespace MongoDB.Bson.Serialization
  28. {
  29. /// <summary>
  30. /// A static class that represents the BSON serialization functionality.
  31. /// </summary>
  32. public static class BsonSerializer
  33. {
  34. // private static fields
  35. private static ReaderWriterLockSlim __configLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
  36. private static Dictionary<Type, IIdGenerator> __idGenerators = new Dictionary<Type, IIdGenerator>();
  37. private static Dictionary<Type, IDiscriminatorConvention> __discriminatorConventions = new Dictionary<Type, IDiscriminatorConvention>();
  38. private static Dictionary<BsonValue, HashSet<Type>> __discriminators = new Dictionary<BsonValue, HashSet<Type>>();
  39. private static HashSet<Type> __discriminatedTypes = new HashSet<Type>();
  40. private static BsonSerializerRegistry __serializerRegistry;
  41. private static TypeMappingSerializationProvider __typeMappingSerializationProvider;
  42. // ConcurrentDictionary<Type, object> is being used as a concurrent set of Type. The values will always be null.
  43. private static ConcurrentDictionary<Type, object> __typesWithRegisteredKnownTypes = new ConcurrentDictionary<Type, object>();
  44. private static bool __useNullIdChecker = false;
  45. private static bool __useZeroIdChecker = false;
  46. // static constructor
  47. static BsonSerializer()
  48. {
  49. CreateSerializerRegistry();
  50. RegisterIdGenerators();
  51. }
  52. // public static properties
  53. /// <summary>
  54. /// Gets the serializer registry.
  55. /// </summary>
  56. public static IBsonSerializerRegistry SerializerRegistry
  57. {
  58. get { return __serializerRegistry; }
  59. }
  60. /// <summary>
  61. /// Gets or sets whether to use the NullIdChecker on reference Id types that don't have an IdGenerator registered.
  62. /// </summary>
  63. public static bool UseNullIdChecker
  64. {
  65. get { return __useNullIdChecker; }
  66. set { __useNullIdChecker = value; }
  67. }
  68. /// <summary>
  69. /// Gets or sets whether to use the ZeroIdChecker on value Id types that don't have an IdGenerator registered.
  70. /// </summary>
  71. public static bool UseZeroIdChecker
  72. {
  73. get { return __useZeroIdChecker; }
  74. set { __useZeroIdChecker = value; }
  75. }
  76. // internal static properties
  77. internal static ReaderWriterLockSlim ConfigLock
  78. {
  79. get { return __configLock; }
  80. }
  81. // public static methods
  82. /// <summary>
  83. /// Deserializes an object from a BsonDocument.
  84. /// </summary>
  85. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  86. /// <param name="document">The BsonDocument.</param>
  87. /// <param name="configurator">The configurator.</param>
  88. /// <returns>A deserialized value.</returns>
  89. public static TNominalType Deserialize<TNominalType>(BsonDocument document, Action<BsonDeserializationContext.Builder> configurator = null)
  90. {
  91. using (var bsonReader = new BsonDocumentReader(document))
  92. {
  93. return Deserialize<TNominalType>(bsonReader, configurator);
  94. }
  95. }
  96. /// <summary>
  97. /// Deserializes a value.
  98. /// </summary>
  99. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  100. /// <param name="bsonReader">The BsonReader.</param>
  101. /// <param name="configurator">The configurator.</param>
  102. /// <returns>A deserialized value.</returns>
  103. public static TNominalType Deserialize<TNominalType>(IBsonReader bsonReader, Action<BsonDeserializationContext.Builder> configurator = null)
  104. {
  105. var serializer = LookupSerializer<TNominalType>();
  106. var context = BsonDeserializationContext.CreateRoot(bsonReader, configurator);
  107. return serializer.Deserialize(context);
  108. }
  109. /// <summary>
  110. /// Deserializes an object from a BSON byte array.
  111. /// </summary>
  112. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  113. /// <param name="bytes">The BSON byte array.</param>
  114. /// <param name="configurator">The configurator.</param>
  115. /// <returns>A deserialized value.</returns>
  116. public static TNominalType Deserialize<TNominalType>(byte[] bytes, Action<BsonDeserializationContext.Builder> configurator = null)
  117. {
  118. using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true))
  119. using (var stream = new ByteBufferStream(buffer))
  120. {
  121. return Deserialize<TNominalType>(stream, configurator);
  122. }
  123. }
  124. /// <summary>
  125. /// Deserializes an object from a BSON Stream.
  126. /// </summary>
  127. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  128. /// <param name="stream">The BSON Stream.</param>
  129. /// <param name="configurator">The configurator.</param>
  130. /// <returns>A deserialized value.</returns>
  131. public static TNominalType Deserialize<TNominalType>(Stream stream, Action<BsonDeserializationContext.Builder> configurator = null)
  132. {
  133. using (var bsonReader = new BsonBinaryReader(stream))
  134. {
  135. return Deserialize<TNominalType>(bsonReader, configurator);
  136. }
  137. }
  138. /// <summary>
  139. /// Deserializes an object from a JSON string.
  140. /// </summary>
  141. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  142. /// <param name="json">The JSON string.</param>
  143. /// <param name="configurator">The configurator.</param>
  144. /// <returns>A deserialized value.</returns>
  145. public static TNominalType Deserialize<TNominalType>(string json, Action<BsonDeserializationContext.Builder> configurator = null)
  146. {
  147. using (var bsonReader = new JsonReader(json))
  148. {
  149. return Deserialize<TNominalType>(bsonReader, configurator);
  150. }
  151. }
  152. /// <summary>
  153. /// Deserializes an object from a JSON TextReader.
  154. /// </summary>
  155. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  156. /// <param name="textReader">The JSON TextReader.</param>
  157. /// <param name="configurator">The configurator.</param>
  158. /// <returns>A deserialized value.</returns>
  159. public static TNominalType Deserialize<TNominalType>(TextReader textReader, Action<BsonDeserializationContext.Builder> configurator = null)
  160. {
  161. using (var bsonReader = new JsonReader(textReader))
  162. {
  163. return Deserialize<TNominalType>(bsonReader, configurator);
  164. }
  165. }
  166. /// <summary>
  167. /// Deserializes an object from a BsonDocument.
  168. /// </summary>
  169. /// <param name="document">The BsonDocument.</param>
  170. /// <param name="nominalType">The nominal type of the object.</param>
  171. /// <param name="configurator">The configurator.</param>
  172. /// <returns>A deserialized value.</returns>
  173. public static object Deserialize(BsonDocument document, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  174. {
  175. using (var bsonReader = new BsonDocumentReader(document))
  176. {
  177. return Deserialize(bsonReader, nominalType, configurator);
  178. }
  179. }
  180. /// <summary>
  181. /// Deserializes a value.
  182. /// </summary>
  183. /// <param name="bsonReader">The BsonReader.</param>
  184. /// <param name="nominalType">The nominal type of the object.</param>
  185. /// <param name="configurator">The configurator.</param>
  186. /// <returns>A deserialized value.</returns>
  187. public static object Deserialize(IBsonReader bsonReader, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  188. {
  189. var serializer = LookupSerializer(nominalType);
  190. var context = BsonDeserializationContext.CreateRoot(bsonReader, configurator);
  191. return serializer.Deserialize(context);
  192. }
  193. /// <summary>
  194. /// Deserializes an object from a BSON byte array.
  195. /// </summary>
  196. /// <param name="bytes">The BSON byte array.</param>
  197. /// <param name="nominalType">The nominal type of the object.</param>
  198. /// <param name="configurator">The configurator.</param>
  199. /// <returns>A deserialized value.</returns>
  200. public static object Deserialize(byte[] bytes, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  201. {
  202. using (var buffer = new ByteArrayBuffer(bytes, isReadOnly: true))
  203. using (var stream = new ByteBufferStream(buffer))
  204. {
  205. return Deserialize(stream, nominalType, configurator);
  206. }
  207. }
  208. /// <summary>
  209. /// Deserializes an object from a BSON Stream.
  210. /// </summary>
  211. /// <param name="stream">The BSON Stream.</param>
  212. /// <param name="nominalType">The nominal type of the object.</param>
  213. /// <param name="configurator">The configurator.</param>
  214. /// <returns>A deserialized value.</returns>
  215. public static object Deserialize(Stream stream, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  216. {
  217. using (var bsonReader = new BsonBinaryReader(stream))
  218. {
  219. return Deserialize(bsonReader, nominalType, configurator);
  220. }
  221. }
  222. /// <summary>
  223. /// Deserializes an object from a JSON string.
  224. /// </summary>
  225. /// <param name="json">The JSON string.</param>
  226. /// <param name="nominalType">The nominal type of the object.</param>
  227. /// <param name="configurator">The configurator.</param>
  228. /// <returns>A deserialized value.</returns>
  229. public static object Deserialize(string json, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  230. {
  231. using (var bsonReader = new JsonReader(json))
  232. {
  233. return Deserialize(bsonReader, nominalType, configurator);
  234. }
  235. }
  236. /// <summary>
  237. /// Deserializes an object from a JSON TextReader.
  238. /// </summary>
  239. /// <param name="textReader">The JSON TextReader.</param>
  240. /// <param name="nominalType">The nominal type of the object.</param>
  241. /// <param name="configurator">The configurator.</param>
  242. /// <returns>A deserialized value.</returns>
  243. public static object Deserialize(TextReader textReader, Type nominalType, Action<BsonDeserializationContext.Builder> configurator = null)
  244. {
  245. using (var bsonReader = new JsonReader(textReader))
  246. {
  247. return Deserialize(bsonReader, nominalType, configurator);
  248. }
  249. }
  250. /// <summary>
  251. /// Returns whether the given type has any discriminators registered for any of its subclasses.
  252. /// </summary>
  253. /// <param name="type">A Type.</param>
  254. /// <returns>True if the type is discriminated.</returns>
  255. public static bool IsTypeDiscriminated(Type type)
  256. {
  257. var typeInfo = type.GetTypeInfo();
  258. return typeInfo.IsInterface || __discriminatedTypes.Contains(type);
  259. }
  260. /// <summary>
  261. /// Looks up the actual type of an object to be deserialized.
  262. /// </summary>
  263. /// <param name="nominalType">The nominal type of the object.</param>
  264. /// <param name="discriminator">The discriminator.</param>
  265. /// <returns>The actual type of the object.</returns>
  266. public static Type LookupActualType(Type nominalType, BsonValue discriminator)
  267. {
  268. if (discriminator == null)
  269. {
  270. return nominalType;
  271. }
  272. // note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock
  273. EnsureKnownTypesAreRegistered(nominalType);
  274. __configLock.EnterReadLock();
  275. try
  276. {
  277. Type actualType = null;
  278. HashSet<Type> hashSet;
  279. var nominalTypeInfo = nominalType.GetTypeInfo();
  280. if (__discriminators.TryGetValue(discriminator, out hashSet))
  281. {
  282. foreach (var type in hashSet)
  283. {
  284. if (nominalTypeInfo.IsAssignableFrom(type))
  285. {
  286. if (actualType == null)
  287. {
  288. actualType = type;
  289. }
  290. else
  291. {
  292. string message = string.Format("Ambiguous discriminator '{0}'.", discriminator);
  293. throw new BsonSerializationException(message);
  294. }
  295. }
  296. }
  297. // no need for additional checks, we found the right type
  298. if (actualType != null)
  299. {
  300. return actualType;
  301. }
  302. }
  303. if (discriminator.IsString)
  304. {
  305. actualType = TypeNameDiscriminator.GetActualType(discriminator.AsString); // see if it's a Type name
  306. }
  307. if (actualType == null)
  308. {
  309. string message = string.Format("Unknown discriminator value '{0}'.", discriminator);
  310. throw new BsonSerializationException(message);
  311. }
  312. if (!nominalTypeInfo.IsAssignableFrom(actualType))
  313. {
  314. string message = string.Format(
  315. "Actual type {0} is not assignable to expected type {1}.",
  316. actualType.FullName, nominalType.FullName);
  317. throw new BsonSerializationException(message);
  318. }
  319. return actualType;
  320. }
  321. finally
  322. {
  323. __configLock.ExitReadLock();
  324. }
  325. }
  326. /// <summary>
  327. /// Looks up the discriminator convention for a type.
  328. /// </summary>
  329. /// <param name="type">The type.</param>
  330. /// <returns>A discriminator convention.</returns>
  331. public static IDiscriminatorConvention LookupDiscriminatorConvention(Type type)
  332. {
  333. __configLock.EnterReadLock();
  334. try
  335. {
  336. IDiscriminatorConvention convention;
  337. if (__discriminatorConventions.TryGetValue(type, out convention))
  338. {
  339. return convention;
  340. }
  341. }
  342. finally
  343. {
  344. __configLock.ExitReadLock();
  345. }
  346. __configLock.EnterWriteLock();
  347. try
  348. {
  349. IDiscriminatorConvention convention;
  350. if (!__discriminatorConventions.TryGetValue(type, out convention))
  351. {
  352. var typeInfo = type.GetTypeInfo();
  353. if (type == typeof(object))
  354. {
  355. // if there is no convention registered for object register the default one
  356. convention = new ObjectDiscriminatorConvention("_t");
  357. RegisterDiscriminatorConvention(typeof(object), convention);
  358. }
  359. else if (typeInfo.IsInterface)
  360. {
  361. // TODO: should convention for interfaces be inherited from parent interfaces?
  362. convention = LookupDiscriminatorConvention(typeof(object));
  363. RegisterDiscriminatorConvention(type, convention);
  364. }
  365. else
  366. {
  367. // inherit the discriminator convention from the closest parent (that isn't object) that has one
  368. // otherwise default to the standard hierarchical convention
  369. Type parentType = typeInfo.BaseType;
  370. while (true)
  371. {
  372. if (parentType == typeof(object))
  373. {
  374. convention = StandardDiscriminatorConvention.Hierarchical;
  375. break;
  376. }
  377. if (__discriminatorConventions.TryGetValue(parentType, out convention))
  378. {
  379. break;
  380. }
  381. parentType = parentType.GetTypeInfo().BaseType;
  382. }
  383. // register this convention for all types between this and the parent type where we found the convention
  384. var unregisteredType = type;
  385. while (unregisteredType != parentType)
  386. {
  387. RegisterDiscriminatorConvention(unregisteredType, convention);
  388. unregisteredType = unregisteredType.GetTypeInfo().BaseType;
  389. }
  390. }
  391. }
  392. return convention;
  393. }
  394. finally
  395. {
  396. __configLock.ExitWriteLock();
  397. }
  398. }
  399. /// <summary>
  400. /// Looks up an IdGenerator.
  401. /// </summary>
  402. /// <param name="type">The Id type.</param>
  403. /// <returns>An IdGenerator for the Id type.</returns>
  404. public static IIdGenerator LookupIdGenerator(Type type)
  405. {
  406. __configLock.EnterReadLock();
  407. try
  408. {
  409. IIdGenerator idGenerator;
  410. if (__idGenerators.TryGetValue(type, out idGenerator))
  411. {
  412. return idGenerator;
  413. }
  414. }
  415. finally
  416. {
  417. __configLock.ExitReadLock();
  418. }
  419. __configLock.EnterWriteLock();
  420. try
  421. {
  422. IIdGenerator idGenerator;
  423. if (!__idGenerators.TryGetValue(type, out idGenerator))
  424. {
  425. var typeInfo = type.GetTypeInfo();
  426. if (typeInfo.IsValueType && __useZeroIdChecker)
  427. {
  428. var iEquatableDefinition = typeof(IEquatable<>);
  429. var iEquatableType = iEquatableDefinition.MakeGenericType(type);
  430. if (iEquatableType.GetTypeInfo().IsAssignableFrom(type))
  431. {
  432. var zeroIdCheckerDefinition = typeof(ZeroIdChecker<>);
  433. var zeroIdCheckerType = zeroIdCheckerDefinition.MakeGenericType(type);
  434. idGenerator = (IIdGenerator)Activator.CreateInstance(zeroIdCheckerType);
  435. }
  436. }
  437. else if (__useNullIdChecker)
  438. {
  439. idGenerator = NullIdChecker.Instance;
  440. }
  441. else
  442. {
  443. idGenerator = null;
  444. }
  445. __idGenerators[type] = idGenerator; // remember it even if it's null
  446. }
  447. return idGenerator;
  448. }
  449. finally
  450. {
  451. __configLock.ExitWriteLock();
  452. }
  453. }
  454. /// <summary>
  455. /// Looks up a serializer for a Type.
  456. /// </summary>
  457. /// <typeparam name="T">The type.</typeparam>
  458. /// <returns>A serializer for type T.</returns>
  459. public static IBsonSerializer<T> LookupSerializer<T>()
  460. {
  461. return (IBsonSerializer<T>)LookupSerializer(typeof(T));
  462. }
  463. /// <summary>
  464. /// Looks up a serializer for a Type.
  465. /// </summary>
  466. /// <param name="type">The Type.</param>
  467. /// <returns>A serializer for the Type.</returns>
  468. public static IBsonSerializer LookupSerializer(Type type)
  469. {
  470. return __serializerRegistry.GetSerializer(type);
  471. }
  472. /// <summary>
  473. /// Registers the discriminator for a type.
  474. /// </summary>
  475. /// <param name="type">The type.</param>
  476. /// <param name="discriminator">The discriminator.</param>
  477. public static void RegisterDiscriminator(Type type, BsonValue discriminator)
  478. {
  479. var typeInfo = type.GetTypeInfo();
  480. if (typeInfo.IsInterface)
  481. {
  482. var message = string.Format("Discriminators can only be registered for classes, not for interface {0}.", type.FullName);
  483. throw new BsonSerializationException(message);
  484. }
  485. __configLock.EnterWriteLock();
  486. try
  487. {
  488. HashSet<Type> hashSet;
  489. if (!__discriminators.TryGetValue(discriminator, out hashSet))
  490. {
  491. hashSet = new HashSet<Type>();
  492. __discriminators.Add(discriminator, hashSet);
  493. }
  494. if (!hashSet.Contains(type))
  495. {
  496. hashSet.Add(type);
  497. // mark all base types as discriminated (so we know that it's worth reading a discriminator)
  498. for (var baseType = typeInfo.BaseType; baseType != null; baseType = baseType.GetTypeInfo().BaseType)
  499. {
  500. __discriminatedTypes.Add(baseType);
  501. }
  502. }
  503. }
  504. finally
  505. {
  506. __configLock.ExitWriteLock();
  507. }
  508. }
  509. /// <summary>
  510. /// Registers the discriminator convention for a type.
  511. /// </summary>
  512. /// <param name="type">Type type.</param>
  513. /// <param name="convention">The discriminator convention.</param>
  514. public static void RegisterDiscriminatorConvention(Type type, IDiscriminatorConvention convention)
  515. {
  516. __configLock.EnterWriteLock();
  517. try
  518. {
  519. if (!__discriminatorConventions.ContainsKey(type))
  520. {
  521. __discriminatorConventions.Add(type, convention);
  522. }
  523. else
  524. {
  525. var message = string.Format("There is already a discriminator convention registered for type {0}.", type.FullName);
  526. throw new BsonSerializationException(message);
  527. }
  528. }
  529. finally
  530. {
  531. __configLock.ExitWriteLock();
  532. }
  533. }
  534. /// <summary>
  535. /// Registers a generic serializer definition for a generic type.
  536. /// </summary>
  537. /// <param name="genericTypeDefinition">The generic type.</param>
  538. /// <param name="genericSerializerDefinition">The generic serializer definition.</param>
  539. public static void RegisterGenericSerializerDefinition(
  540. Type genericTypeDefinition,
  541. Type genericSerializerDefinition)
  542. {
  543. __typeMappingSerializationProvider.RegisterMapping(genericTypeDefinition, genericSerializerDefinition);
  544. }
  545. /// <summary>
  546. /// Registers an IdGenerator for an Id Type.
  547. /// </summary>
  548. /// <param name="type">The Id Type.</param>
  549. /// <param name="idGenerator">The IdGenerator for the Id Type.</param>
  550. public static void RegisterIdGenerator(Type type, IIdGenerator idGenerator)
  551. {
  552. __configLock.EnterWriteLock();
  553. try
  554. {
  555. __idGenerators[type] = idGenerator;
  556. }
  557. finally
  558. {
  559. __configLock.ExitWriteLock();
  560. }
  561. }
  562. /// <summary>
  563. /// Registers a serialization provider.
  564. /// </summary>
  565. /// <param name="provider">The serialization provider.</param>
  566. public static void RegisterSerializationProvider(IBsonSerializationProvider provider)
  567. {
  568. __serializerRegistry.RegisterSerializationProvider(provider);
  569. }
  570. /// <summary>
  571. /// Registers a serializer for a type.
  572. /// </summary>
  573. /// <typeparam name="T">The type.</typeparam>
  574. /// <param name="serializer">The serializer.</param>
  575. public static void RegisterSerializer<T>(IBsonSerializer<T> serializer)
  576. {
  577. RegisterSerializer(typeof(T), serializer);
  578. }
  579. /// <summary>
  580. /// Registers a serializer for a type.
  581. /// </summary>
  582. /// <param name="type">The type.</param>
  583. /// <param name="serializer">The serializer.</param>
  584. public static void RegisterSerializer(Type type, IBsonSerializer serializer)
  585. {
  586. __serializerRegistry.RegisterSerializer(type, serializer);
  587. }
  588. /// <summary>
  589. /// Serializes a value.
  590. /// </summary>
  591. /// <typeparam name="TNominalType">The nominal type of the object.</typeparam>
  592. /// <param name="bsonWriter">The BsonWriter.</param>
  593. /// <param name="value">The object.</param>
  594. /// <param name="configurator">The serialization context configurator.</param>
  595. /// <param name="args">The serialization args.</param>
  596. public static void Serialize<TNominalType>(
  597. IBsonWriter bsonWriter,
  598. TNominalType value,
  599. Action<BsonSerializationContext.Builder> configurator = null,
  600. BsonSerializationArgs args = default(BsonSerializationArgs))
  601. {
  602. args.SetOrValidateNominalType(typeof(TNominalType), "<TNominalType>");
  603. var serializer = LookupSerializer<TNominalType>();
  604. var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator);
  605. serializer.Serialize(context, args, value);
  606. }
  607. /// <summary>
  608. /// Serializes a value.
  609. /// </summary>
  610. /// <param name="bsonWriter">The BsonWriter.</param>
  611. /// <param name="nominalType">The nominal type of the object.</param>
  612. /// <param name="value">The object.</param>
  613. /// <param name="configurator">The serialization context configurator.</param>
  614. /// <param name="args">The serialization args.</param>
  615. public static void Serialize(
  616. IBsonWriter bsonWriter,
  617. Type nominalType,
  618. object value,
  619. Action<BsonSerializationContext.Builder> configurator = null,
  620. BsonSerializationArgs args = default(BsonSerializationArgs))
  621. {
  622. args.SetOrValidateNominalType(nominalType, "nominalType");
  623. var serializer = LookupSerializer(nominalType);
  624. var context = BsonSerializationContext.CreateRoot(bsonWriter, configurator);
  625. serializer.Serialize(context, args, value);
  626. }
  627. // internal static methods
  628. internal static void EnsureKnownTypesAreRegistered(Type nominalType)
  629. {
  630. if (__typesWithRegisteredKnownTypes.ContainsKey(nominalType))
  631. {
  632. return;
  633. }
  634. __configLock.EnterWriteLock();
  635. try
  636. {
  637. if (!__typesWithRegisteredKnownTypes.ContainsKey(nominalType))
  638. {
  639. // only call LookupClassMap for classes with a BsonKnownTypesAttribute
  640. var hasKnownTypesAttribute = nominalType.GetTypeInfo().GetCustomAttributes(typeof(BsonKnownTypesAttribute), inherit: false).Any();
  641. if (hasKnownTypesAttribute)
  642. {
  643. // try and force a scan of the known types
  644. LookupSerializer(nominalType);
  645. }
  646. // NOTE: The nominalType MUST be added to __typesWithRegisteredKnownTypes after all registration
  647. // work is done to ensure that other threads don't access a partially registered nominalType
  648. // when performing the initial check above outside the __config lock.
  649. __typesWithRegisteredKnownTypes[nominalType] = null;
  650. }
  651. }
  652. finally
  653. {
  654. __configLock.ExitWriteLock();
  655. }
  656. }
  657. // private static methods
  658. private static void CreateSerializerRegistry()
  659. {
  660. __serializerRegistry = new BsonSerializerRegistry();
  661. __typeMappingSerializationProvider = new TypeMappingSerializationProvider();
  662. // order matters. It's in reverse order of how they'll get consumed
  663. __serializerRegistry.RegisterSerializationProvider(new BsonClassMapSerializationProvider());
  664. __serializerRegistry.RegisterSerializationProvider(new DiscriminatedInterfaceSerializationProvider());
  665. __serializerRegistry.RegisterSerializationProvider(new CollectionsSerializationProvider());
  666. __serializerRegistry.RegisterSerializationProvider(new PrimitiveSerializationProvider());
  667. __serializerRegistry.RegisterSerializationProvider(new AttributedSerializationProvider());
  668. __serializerRegistry.RegisterSerializationProvider(__typeMappingSerializationProvider);
  669. __serializerRegistry.RegisterSerializationProvider(new BsonObjectModelSerializationProvider());
  670. }
  671. private static void RegisterIdGenerators()
  672. {
  673. RegisterIdGenerator(typeof(BsonObjectId), BsonObjectIdGenerator.Instance);
  674. RegisterIdGenerator(typeof(Guid), GuidGenerator.Instance);
  675. RegisterIdGenerator(typeof(ObjectId), ObjectIdGenerator.Instance);
  676. }
  677. }
  678. }