DiscriminatedWrapperSerializer.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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 that serializes values as a discriminator/value pair.
  22. /// </summary>
  23. /// <typeparam name="TValue">The type of the value.</typeparam>
  24. public class DiscriminatedWrapperSerializer<TValue> : SerializerBase<TValue>
  25. {
  26. // private constants
  27. private static class Flags
  28. {
  29. public const long Discriminator = 1;
  30. public const long Value = 2;
  31. public const long Other = 4;
  32. }
  33. // private fields
  34. private readonly IDiscriminatorConvention _discriminatorConvention;
  35. private readonly SerializerHelper _helper;
  36. private readonly SerializerHelper _isPositionedHelper;
  37. private readonly IBsonSerializer<TValue> _wrappedSerializer;
  38. // constructors
  39. /// <summary>
  40. /// Initializes a new instance of the <see cref="DiscriminatedWrapperSerializer{TValue}" /> class.
  41. /// </summary>
  42. /// <param name="discriminatorConvention">The discriminator convention.</param>
  43. /// <param name="wrappedSerializer">The wrapped serializer.</param>
  44. public DiscriminatedWrapperSerializer(IDiscriminatorConvention discriminatorConvention, IBsonSerializer<TValue> wrappedSerializer)
  45. {
  46. _discriminatorConvention = discriminatorConvention;
  47. _wrappedSerializer = wrappedSerializer;
  48. _helper = new SerializerHelper
  49. (
  50. new SerializerHelper.Member(discriminatorConvention.ElementName, Flags.Discriminator),
  51. new SerializerHelper.Member("_v", Flags.Value)
  52. );
  53. _isPositionedHelper = new SerializerHelper
  54. (
  55. new SerializerHelper.Member(discriminatorConvention.ElementName, Flags.Discriminator, isOptional: true),
  56. new SerializerHelper.Member("_v", Flags.Value, isOptional: true),
  57. new SerializerHelper.Member("*", Flags.Other, isOptional: true)
  58. );
  59. }
  60. // public methods
  61. /// <summary>
  62. /// Deserializes a value.
  63. /// </summary>
  64. /// <param name="context">The deserialization context.</param>
  65. /// <param name="args">The deserialization args.</param>
  66. /// <returns>A deserialized value.</returns>
  67. public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
  68. {
  69. var bsonReader = context.Reader;
  70. var nominalType = args.NominalType;
  71. var actualType = _discriminatorConvention.GetActualType(bsonReader, nominalType);
  72. var serializer = BsonSerializer.LookupSerializer(actualType);
  73. TValue value = default(TValue);
  74. _helper.DeserializeMembers(context, (elementName, flag) =>
  75. {
  76. switch (flag)
  77. {
  78. case Flags.Discriminator:
  79. bsonReader.SkipValue();
  80. break;
  81. case Flags.Value:
  82. var valueDeserializationArgs = new BsonDeserializationArgs { NominalType = actualType };
  83. value = (TValue)serializer.Deserialize(context, valueDeserializationArgs);
  84. break;
  85. }
  86. });
  87. return value;
  88. }
  89. /// <summary>
  90. /// Determines whether the reader is positioned at a discriminated wrapper.
  91. /// </summary>
  92. /// <param name="context">The context.</param>
  93. /// <returns>True if the reader is positioned at a discriminated wrapper.</returns>
  94. public bool IsPositionedAtDiscriminatedWrapper(BsonDeserializationContext context)
  95. {
  96. var bsonReader = context.Reader;
  97. var bookmark = bsonReader.GetBookmark();
  98. try
  99. {
  100. if (bsonReader.GetCurrentBsonType() != BsonType.Document) { return false; }
  101. var foundFields = _isPositionedHelper.DeserializeMembers(context, (elementName, flag) =>
  102. {
  103. context.Reader.SkipValue();
  104. });
  105. return foundFields == (Flags.Discriminator | Flags.Value);
  106. }
  107. finally
  108. {
  109. bsonReader.ReturnToBookmark(bookmark);
  110. }
  111. }
  112. /// <summary>
  113. /// Serializes a value.
  114. /// </summary>
  115. /// <param name="context">The serialization context.</param>
  116. /// <param name="args">The serialization args.</param>
  117. /// <param name="value">The value.</param>
  118. public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValue value)
  119. {
  120. var bsonWriter = context.Writer;
  121. var nominalType = args.NominalType;
  122. var actualType = value.GetType();
  123. var discriminator = _discriminatorConvention.GetDiscriminator(nominalType, actualType);
  124. bsonWriter.WriteStartDocument();
  125. bsonWriter.WriteName(_discriminatorConvention.ElementName);
  126. BsonValueSerializer.Instance.Serialize(context, discriminator);
  127. bsonWriter.WriteName("_v");
  128. args.NominalType = actualType;
  129. _wrappedSerializer.Serialize(context, args, value);
  130. bsonWriter.WriteEndDocument();
  131. }
  132. }
  133. }