ObjectSerializer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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 MongoDB.Bson.IO;
  17. using MongoDB.Bson.Serialization.Conventions;
  18. namespace MongoDB.Bson.Serialization.Serializers
  19. {
  20. /// <summary>
  21. /// Represents a serializer for objects.
  22. /// </summary>
  23. public class ObjectSerializer : ClassSerializerBase<object>
  24. {
  25. // private static fields
  26. private static readonly ObjectSerializer __instance = new ObjectSerializer();
  27. // private fields
  28. private readonly IDiscriminatorConvention _discriminatorConvention;
  29. // constructors
  30. /// <summary>
  31. /// Initializes a new instance of the <see cref="ObjectSerializer"/> class.
  32. /// </summary>
  33. public ObjectSerializer()
  34. : this(BsonSerializer.LookupDiscriminatorConvention(typeof(object)))
  35. {
  36. }
  37. /// <summary>
  38. /// Initializes a new instance of the <see cref="ObjectSerializer"/> class.
  39. /// </summary>
  40. /// <param name="discriminatorConvention">The discriminator convention.</param>
  41. /// <exception cref="System.ArgumentNullException">discriminatorConvention</exception>
  42. public ObjectSerializer(IDiscriminatorConvention discriminatorConvention)
  43. {
  44. if (discriminatorConvention == null)
  45. {
  46. throw new ArgumentNullException("discriminatorConvention");
  47. }
  48. _discriminatorConvention = discriminatorConvention;
  49. }
  50. // public static properties
  51. /// <summary>
  52. /// Gets the standard instance.
  53. /// </summary>
  54. /// <value>
  55. /// The standard instance.
  56. /// </value>
  57. public static ObjectSerializer Instance
  58. {
  59. get { return __instance; }
  60. }
  61. // public methods
  62. /// <summary>
  63. /// Deserializes a value.
  64. /// </summary>
  65. /// <param name="context">The deserialization context.</param>
  66. /// <param name="args">The deserialization args.</param>
  67. /// <returns>A deserialized value.</returns>
  68. public override object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
  69. {
  70. var bsonReader = context.Reader;
  71. var bsonType = bsonReader.GetCurrentBsonType();
  72. switch (bsonType)
  73. {
  74. case BsonType.Array:
  75. if (context.DynamicArraySerializer != null)
  76. {
  77. return context.DynamicArraySerializer.Deserialize(context);
  78. }
  79. goto default;
  80. case BsonType.Binary:
  81. var binaryData = bsonReader.ReadBinaryData();
  82. var subType = binaryData.SubType;
  83. if (subType == BsonBinarySubType.UuidStandard || subType == BsonBinarySubType.UuidLegacy)
  84. {
  85. return binaryData.ToGuid();
  86. }
  87. goto default;
  88. case BsonType.Boolean:
  89. return bsonReader.ReadBoolean();
  90. case BsonType.DateTime:
  91. var millisecondsSinceEpoch = bsonReader.ReadDateTime();
  92. var bsonDateTime = new BsonDateTime(millisecondsSinceEpoch);
  93. return bsonDateTime.ToUniversalTime();
  94. case BsonType.Decimal128:
  95. return bsonReader.ReadDecimal128();
  96. case BsonType.Document:
  97. return DeserializeDiscriminatedValue(context, args);
  98. case BsonType.Double:
  99. return bsonReader.ReadDouble();
  100. case BsonType.Int32:
  101. return bsonReader.ReadInt32();
  102. case BsonType.Int64:
  103. return bsonReader.ReadInt64();
  104. case BsonType.Null:
  105. bsonReader.ReadNull();
  106. return null;
  107. case BsonType.ObjectId:
  108. return bsonReader.ReadObjectId();
  109. case BsonType.String:
  110. return bsonReader.ReadString();
  111. default:
  112. var message = string.Format("ObjectSerializer does not support BSON type '{0}'.", bsonType);
  113. throw new FormatException(message);
  114. }
  115. }
  116. /// <summary>
  117. /// Serializes a value.
  118. /// </summary>
  119. /// <param name="context">The serialization context.</param>
  120. /// <param name="args">The serialization args.</param>
  121. /// <param name="value">The object.</param>
  122. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
  123. {
  124. var bsonWriter = context.Writer;
  125. if (value == null)
  126. {
  127. bsonWriter.WriteNull();
  128. }
  129. else
  130. {
  131. var actualType = value.GetType();
  132. if (actualType == typeof(object))
  133. {
  134. bsonWriter.WriteStartDocument();
  135. bsonWriter.WriteEndDocument();
  136. }
  137. else
  138. {
  139. // certain types can be written directly as BSON value
  140. // if we're not at the top level document, or if we're using the JsonWriter
  141. if (bsonWriter.State == BsonWriterState.Value || bsonWriter is JsonWriter)
  142. {
  143. switch (Type.GetTypeCode(actualType))
  144. {
  145. case TypeCode.Boolean:
  146. bsonWriter.WriteBoolean((bool)value);
  147. return;
  148. case TypeCode.DateTime:
  149. // TODO: is this right? will lose precision after round trip
  150. var bsonDateTime = new BsonDateTime(BsonUtils.ToUniversalTime((DateTime)value));
  151. bsonWriter.WriteDateTime(bsonDateTime.MillisecondsSinceEpoch);
  152. return;
  153. case TypeCode.Double:
  154. bsonWriter.WriteDouble((double)value);
  155. return;
  156. case TypeCode.Int16:
  157. // TODO: is this right? will change type to Int32 after round trip
  158. bsonWriter.WriteInt32((short)value);
  159. return;
  160. case TypeCode.Int32:
  161. bsonWriter.WriteInt32((int)value);
  162. return;
  163. case TypeCode.Int64:
  164. bsonWriter.WriteInt64((long)value);
  165. return;
  166. case TypeCode.Object:
  167. if (actualType == typeof(Decimal128))
  168. {
  169. var decimal128 = (Decimal128)value;
  170. bsonWriter.WriteDecimal128(decimal128);
  171. return;
  172. }
  173. if (actualType == typeof(Guid))
  174. {
  175. var guid = (Guid)value;
  176. var guidRepresentation = bsonWriter.Settings.GuidRepresentation;
  177. var binaryData = new BsonBinaryData(guid, guidRepresentation);
  178. bsonWriter.WriteBinaryData(binaryData);
  179. return;
  180. }
  181. if (actualType == typeof(ObjectId))
  182. {
  183. bsonWriter.WriteObjectId((ObjectId)value);
  184. return;
  185. }
  186. break;
  187. case TypeCode.String:
  188. bsonWriter.WriteString((string)value);
  189. return;
  190. }
  191. }
  192. SerializeDiscriminatedValue(context, args, value, actualType);
  193. }
  194. }
  195. }
  196. // private methods
  197. private object DeserializeDiscriminatedValue(BsonDeserializationContext context, BsonDeserializationArgs args)
  198. {
  199. var bsonReader = context.Reader;
  200. var actualType = _discriminatorConvention.GetActualType(bsonReader, typeof(object));
  201. if (actualType == typeof(object))
  202. {
  203. var type = bsonReader.GetCurrentBsonType();
  204. switch(type)
  205. {
  206. case BsonType.Document:
  207. if (context.DynamicDocumentSerializer != null)
  208. {
  209. return context.DynamicDocumentSerializer.Deserialize(context, args);
  210. }
  211. break;
  212. }
  213. bsonReader.ReadStartDocument();
  214. bsonReader.ReadEndDocument();
  215. return new object();
  216. }
  217. else
  218. {
  219. var serializer = BsonSerializer.LookupSerializer(actualType);
  220. var polymorphicSerializer = serializer as IBsonPolymorphicSerializer;
  221. if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer)
  222. {
  223. return serializer.Deserialize(context, args);
  224. }
  225. else
  226. {
  227. object value = null;
  228. var wasValuePresent = false;
  229. bsonReader.ReadStartDocument();
  230. while (bsonReader.ReadBsonType() != 0)
  231. {
  232. var name = bsonReader.ReadName();
  233. if (name == _discriminatorConvention.ElementName)
  234. {
  235. bsonReader.SkipValue();
  236. }
  237. else if (name == "_v")
  238. {
  239. value = serializer.Deserialize(context);
  240. wasValuePresent = true;
  241. }
  242. else
  243. {
  244. var message = string.Format("Unexpected element name: '{0}'.", name);
  245. throw new FormatException(message);
  246. }
  247. }
  248. bsonReader.ReadEndDocument();
  249. if (!wasValuePresent)
  250. {
  251. throw new FormatException("_v element missing.");
  252. }
  253. return value;
  254. }
  255. }
  256. }
  257. private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType)
  258. {
  259. var serializer = BsonSerializer.LookupSerializer(actualType);
  260. var polymorphicSerializer = serializer as IBsonPolymorphicSerializer;
  261. if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer)
  262. {
  263. serializer.Serialize(context, args, value);
  264. }
  265. else
  266. {
  267. if (context.IsDynamicType != null && context.IsDynamicType(value.GetType()))
  268. {
  269. args.NominalType = actualType;
  270. serializer.Serialize(context, args, value);
  271. }
  272. else
  273. {
  274. var bsonWriter = context.Writer;
  275. var discriminator = _discriminatorConvention.GetDiscriminator(typeof(object), actualType);
  276. bsonWriter.WriteStartDocument();
  277. bsonWriter.WriteName(_discriminatorConvention.ElementName);
  278. BsonValueSerializer.Instance.Serialize(context, discriminator);
  279. bsonWriter.WriteName("_v");
  280. serializer.Serialize(context, value);
  281. bsonWriter.WriteEndDocument();
  282. }
  283. }
  284. }
  285. }
  286. }