|
|
@@ -0,0 +1,770 @@
|
|
|
+/* Copyright 2010-present MongoDB Inc.
|
|
|
+*
|
|
|
+* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+* you may not use this file except in compliance with the License.
|
|
|
+* You may obtain a copy of the License at
|
|
|
+*
|
|
|
+* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+*
|
|
|
+* Unless required by applicable law or agreed to in writing, software
|
|
|
+* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+* See the License for the specific language governing permissions and
|
|
|
+* limitations under the License.
|
|
|
+*/
|
|
|
+
|
|
|
+using System;
|
|
|
+using System.Collections;
|
|
|
+using System.Collections.Generic;
|
|
|
+using MongoDB.Bson;
|
|
|
+using MongoDB.Bson.IO;
|
|
|
+using MongoDB.Bson.Serialization;
|
|
|
+using MongoDB.Bson.Serialization.Options;
|
|
|
+using MongoDB.Bson.Serialization.Serializers;
|
|
|
+
|
|
|
+namespace ET
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// Represents a serializer for dictionaries.
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="TDictionary">The type of the dictionary.</typeparam>
|
|
|
+ public abstract class BsonSortedDictionarySerializerBase<TDictionary> :
|
|
|
+ ClassSerializerBase<TDictionary>,
|
|
|
+ IBsonDocumentSerializer,
|
|
|
+ IBsonDictionarySerializer
|
|
|
+ where TDictionary : class, IDictionary
|
|
|
+ {
|
|
|
+ // private constants
|
|
|
+ private static class Flags
|
|
|
+ {
|
|
|
+ public const long Key = 1;
|
|
|
+ public const long Value = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ // private fields
|
|
|
+ private readonly DictionaryRepresentation _dictionaryRepresentation;
|
|
|
+ private readonly SerializerHelper _helper;
|
|
|
+ private readonly IBsonSerializer _keySerializer;
|
|
|
+ private readonly IBsonSerializer _valueSerializer;
|
|
|
+
|
|
|
+ // constructors
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary}"/> class.
|
|
|
+ /// </summary>
|
|
|
+ public BsonSortedDictionarySerializerBase()
|
|
|
+ : this(DictionaryRepresentation.Document)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary}"/> class.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dictionaryRepresentation">The dictionary representation.</param>
|
|
|
+ public BsonSortedDictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation)
|
|
|
+ : this(dictionaryRepresentation, BsonSerializer.LookupSerializer<object>(), BsonSerializer.LookupSerializer<object>())
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary}"/> class.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dictionaryRepresentation">The dictionary representation.</param>
|
|
|
+ /// <param name="keySerializer">The key serializer.</param>
|
|
|
+ /// <param name="valueSerializer">The value serializer.</param>
|
|
|
+ public BsonSortedDictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation, IBsonSerializer keySerializer, IBsonSerializer valueSerializer)
|
|
|
+ {
|
|
|
+ _dictionaryRepresentation = dictionaryRepresentation;
|
|
|
+ _keySerializer = keySerializer;
|
|
|
+ _valueSerializer = valueSerializer;
|
|
|
+
|
|
|
+ _helper = new SerializerHelper
|
|
|
+ (
|
|
|
+ new SerializerHelper.Member("k", Flags.Key),
|
|
|
+ new SerializerHelper.Member("v", Flags.Value)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // public properties
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the dictionary representation.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The dictionary representation.
|
|
|
+ /// </value>
|
|
|
+ public DictionaryRepresentation DictionaryRepresentation
|
|
|
+ {
|
|
|
+ get { return _dictionaryRepresentation; }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the key serializer.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The key serializer.
|
|
|
+ /// </value>
|
|
|
+ public IBsonSerializer KeySerializer
|
|
|
+ {
|
|
|
+ get { return _keySerializer; }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the value serializer.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The value serializer.
|
|
|
+ /// </value>
|
|
|
+ public IBsonSerializer ValueSerializer
|
|
|
+ {
|
|
|
+ get { return _valueSerializer; }
|
|
|
+ }
|
|
|
+
|
|
|
+ // public methods
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
|
|
|
+ {
|
|
|
+ if (_dictionaryRepresentation != DictionaryRepresentation.Document)
|
|
|
+ {
|
|
|
+ serializationInfo = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ serializationInfo = new BsonSerializationInfo(
|
|
|
+ memberName,
|
|
|
+ _valueSerializer,
|
|
|
+ _valueSerializer.ValueType);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // protected methods
|
|
|
+ /// <summary>
|
|
|
+ /// Deserializes a value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">The deserialization context.</param>
|
|
|
+ /// <param name="args">The deserialization args.</param>
|
|
|
+ /// <returns>A deserialized value.</returns>
|
|
|
+ protected override TDictionary DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
|
|
|
+ {
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+ var bsonType = bsonReader.GetCurrentBsonType();
|
|
|
+ switch (bsonType)
|
|
|
+ {
|
|
|
+ case BsonType.Array:
|
|
|
+ return DeserializeArrayRepresentation(context);
|
|
|
+ case BsonType.Document:
|
|
|
+ return DeserializeDocumentRepresentation(context);
|
|
|
+ default:
|
|
|
+ throw CreateCannotDeserializeFromBsonTypeException(bsonType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Serializes a value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">The serialization context.</param>
|
|
|
+ /// <param name="args">The serialization args.</param>
|
|
|
+ /// <param name="value">The object.</param>
|
|
|
+ protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+
|
|
|
+ switch (_dictionaryRepresentation)
|
|
|
+ {
|
|
|
+ case DictionaryRepresentation.Document:
|
|
|
+ SerializeDocumentRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case DictionaryRepresentation.ArrayOfArrays:
|
|
|
+ SerializeArrayOfArraysRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case DictionaryRepresentation.ArrayOfDocuments:
|
|
|
+ SerializeArrayOfDocumentsRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ var message = string.Format("'{0}' is not a valid IDictionary representation.", _dictionaryRepresentation);
|
|
|
+ throw new BsonSerializationException(message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // protected methods
|
|
|
+ /// <summary>
|
|
|
+ /// Creates the instance.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The instance.</returns>
|
|
|
+ protected abstract TDictionary CreateInstance();
|
|
|
+
|
|
|
+ // private methods
|
|
|
+ private TDictionary DeserializeArrayRepresentation(BsonDeserializationContext context)
|
|
|
+ {
|
|
|
+ var dictionary = CreateInstance();
|
|
|
+
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+ bsonReader.ReadStartArray();
|
|
|
+ while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
|
|
|
+ {
|
|
|
+ object key;
|
|
|
+ object value;
|
|
|
+
|
|
|
+ var bsonType = bsonReader.GetCurrentBsonType();
|
|
|
+ switch (bsonType)
|
|
|
+ {
|
|
|
+ case BsonType.Array:
|
|
|
+ bsonReader.ReadStartArray();
|
|
|
+ key = _keySerializer.Deserialize(context);
|
|
|
+ value = _valueSerializer.Deserialize(context);
|
|
|
+ bsonReader.ReadEndArray();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case BsonType.Document:
|
|
|
+ key = null;
|
|
|
+ value = null;
|
|
|
+ _helper.DeserializeMembers(context, (elementName, flag) =>
|
|
|
+ {
|
|
|
+ switch (flag)
|
|
|
+ {
|
|
|
+ case Flags.Key: key = _keySerializer.Deserialize(context); break;
|
|
|
+ case Flags.Value: value = _valueSerializer.Deserialize(context); break;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ throw CreateCannotDeserializeFromBsonTypeException(bsonType);
|
|
|
+ }
|
|
|
+
|
|
|
+ dictionary.Add(key, value);
|
|
|
+ }
|
|
|
+ bsonReader.ReadEndArray();
|
|
|
+
|
|
|
+ return dictionary;
|
|
|
+ }
|
|
|
+
|
|
|
+ private TDictionary DeserializeDocumentRepresentation(BsonDeserializationContext context)
|
|
|
+ {
|
|
|
+ var dictionary = CreateInstance();
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+ bsonReader.ReadStartDocument();
|
|
|
+ while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
|
|
|
+ {
|
|
|
+ var key = DeserializeKeyString(bsonReader.ReadName());
|
|
|
+ var value = _valueSerializer.Deserialize(context);
|
|
|
+ dictionary.Add(key, value);
|
|
|
+ }
|
|
|
+ bsonReader.ReadEndDocument();
|
|
|
+ return dictionary;
|
|
|
+ }
|
|
|
+
|
|
|
+ private object DeserializeKeyString(string keyString)
|
|
|
+ {
|
|
|
+ var keyDocument = new BsonDocument("k", keyString);
|
|
|
+ using (var keyReader = new BsonDocumentReader(keyDocument))
|
|
|
+ {
|
|
|
+ var context = BsonDeserializationContext.CreateRoot(keyReader);
|
|
|
+ keyReader.ReadStartDocument();
|
|
|
+ keyReader.ReadName("k");
|
|
|
+ var key = _keySerializer.Deserialize(context);
|
|
|
+ keyReader.ReadEndDocument();
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeArrayOfArraysRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ foreach (DictionaryEntry dictionaryEntry in value)
|
|
|
+ {
|
|
|
+ if (dictionaryEntry.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ _keySerializer.Serialize(context, dictionaryEntry.Key);
|
|
|
+ _valueSerializer.Serialize(context, dictionaryEntry.Value);
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeArrayOfDocumentsRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ foreach (DictionaryEntry dictionaryEntry in value)
|
|
|
+ {
|
|
|
+ if (dictionaryEntry.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteStartDocument();
|
|
|
+ bsonWriter.WriteName("k");
|
|
|
+ _keySerializer.Serialize(context, dictionaryEntry.Key);
|
|
|
+ bsonWriter.WriteName("v");
|
|
|
+ _valueSerializer.Serialize(context, dictionaryEntry.Value);
|
|
|
+ bsonWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeDocumentRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartDocument();
|
|
|
+ foreach (DictionaryEntry dictionaryEntry in value)
|
|
|
+ {
|
|
|
+ if (dictionaryEntry.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteName(SerializeKeyString(dictionaryEntry.Key));
|
|
|
+ _valueSerializer.Serialize(context, dictionaryEntry.Value);
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+
|
|
|
+ private string SerializeKeyString(object key)
|
|
|
+ {
|
|
|
+ var keyDocument = new BsonDocument();
|
|
|
+ using (var keyWriter = new BsonDocumentWriter(keyDocument))
|
|
|
+ {
|
|
|
+ var context = BsonSerializationContext.CreateRoot(keyWriter);
|
|
|
+ keyWriter.WriteStartDocument();
|
|
|
+ keyWriter.WriteName("k");
|
|
|
+ _keySerializer.Serialize(context, key);
|
|
|
+ keyWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+
|
|
|
+ var keyValue = keyDocument["k"];
|
|
|
+ if (keyValue.BsonType != BsonType.String)
|
|
|
+ {
|
|
|
+ throw new BsonSerializationException("When using DictionaryRepresentation.Document key values must serialize as strings.");
|
|
|
+ }
|
|
|
+
|
|
|
+ return (string)keyValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Represents a serializer for dictionaries.
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="TDictionary">The type of the dictionary.</typeparam>
|
|
|
+ /// <typeparam name="TKey">The type of the keys.</typeparam>
|
|
|
+ /// <typeparam name="TValue">The type of the values.</typeparam>
|
|
|
+ public abstract class SortedDictionarySerializerBase<TDictionary, TKey, TValue> :
|
|
|
+ ClassSerializerBase<TDictionary>,
|
|
|
+ IBsonArraySerializer,
|
|
|
+ IBsonDocumentSerializer,
|
|
|
+ IBsonDictionarySerializer
|
|
|
+ where TDictionary : class, IEnumerable<KeyValuePair<TKey, TValue>>
|
|
|
+ {
|
|
|
+ // private constants
|
|
|
+ private static class Flags
|
|
|
+ {
|
|
|
+ public const long Key = 1;
|
|
|
+ public const long Value = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ // private fields
|
|
|
+ private readonly DictionaryRepresentation _dictionaryRepresentation;
|
|
|
+ private readonly SerializerHelper _helper;
|
|
|
+ private readonly Lazy<IBsonSerializer<TKey>> _lazyKeySerializer;
|
|
|
+ private readonly Lazy<IBsonSerializer<TValue>> _lazyValueSerializer;
|
|
|
+
|
|
|
+ // constructors
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary, TKey, TValue}"/> class.
|
|
|
+ /// </summary>
|
|
|
+ public SortedDictionarySerializerBase()
|
|
|
+ : this(DictionaryRepresentation.Document)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary, TKey, TValue}" /> class.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dictionaryRepresentation">The dictionary representation.</param>
|
|
|
+ public SortedDictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation)
|
|
|
+ : this(dictionaryRepresentation, BsonSerializer.SerializerRegistry)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary, TKey, TValue}" /> class.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dictionaryRepresentation">The dictionary representation.</param>
|
|
|
+ /// <param name="keySerializer">The key serializer.</param>
|
|
|
+ /// <param name="valueSerializer">The value serializer.</param>
|
|
|
+ public SortedDictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation, IBsonSerializer<TKey> keySerializer, IBsonSerializer<TValue> valueSerializer)
|
|
|
+ : this(
|
|
|
+ dictionaryRepresentation,
|
|
|
+ new Lazy<IBsonSerializer<TKey>>(() => keySerializer),
|
|
|
+ new Lazy<IBsonSerializer<TValue>>(() => valueSerializer))
|
|
|
+ {
|
|
|
+ if (keySerializer == null)
|
|
|
+ {
|
|
|
+ throw new ArgumentNullException("keySerializer");
|
|
|
+ }
|
|
|
+ if (valueSerializer == null)
|
|
|
+ {
|
|
|
+ throw new ArgumentNullException("valueSerializer");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="DictionarySerializerBase{TDictionary, TKey, TValue}" /> class.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="dictionaryRepresentation">The dictionary representation.</param>
|
|
|
+ /// <param name="serializerRegistry">The serializer registry.</param>
|
|
|
+ public SortedDictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation, IBsonSerializerRegistry serializerRegistry)
|
|
|
+ : this(
|
|
|
+ dictionaryRepresentation,
|
|
|
+ new Lazy<IBsonSerializer<TKey>>(() => serializerRegistry.GetSerializer<TKey>()),
|
|
|
+ new Lazy<IBsonSerializer<TValue>>(() => serializerRegistry.GetSerializer<TValue>()))
|
|
|
+ {
|
|
|
+ if (serializerRegistry == null)
|
|
|
+ {
|
|
|
+ throw new ArgumentNullException("serializerRegistry");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private SortedDictionarySerializerBase(
|
|
|
+ DictionaryRepresentation dictionaryRepresentation,
|
|
|
+ Lazy<IBsonSerializer<TKey>> lazyKeySerializer,
|
|
|
+ Lazy<IBsonSerializer<TValue>> lazyValueSerializer)
|
|
|
+ {
|
|
|
+ _dictionaryRepresentation = dictionaryRepresentation;
|
|
|
+ _lazyKeySerializer = lazyKeySerializer;
|
|
|
+ _lazyValueSerializer = lazyValueSerializer;
|
|
|
+
|
|
|
+ _helper = new SerializerHelper
|
|
|
+ (
|
|
|
+ new SerializerHelper.Member("k", Flags.Key),
|
|
|
+ new SerializerHelper.Member("v", Flags.Value)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // public properties
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the dictionary representation.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The dictionary representation.
|
|
|
+ /// </value>
|
|
|
+ public DictionaryRepresentation DictionaryRepresentation
|
|
|
+ {
|
|
|
+ get { return _dictionaryRepresentation; }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the key serializer.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The key serializer.
|
|
|
+ /// </value>
|
|
|
+ public IBsonSerializer<TKey> KeySerializer
|
|
|
+ {
|
|
|
+ get { return _lazyKeySerializer.Value; }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the value serializer.
|
|
|
+ /// </summary>
|
|
|
+ /// <value>
|
|
|
+ /// The value serializer.
|
|
|
+ /// </value>
|
|
|
+ public IBsonSerializer<TValue> ValueSerializer
|
|
|
+ {
|
|
|
+ get { return _lazyValueSerializer.Value; }
|
|
|
+ }
|
|
|
+
|
|
|
+ // public methods
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
|
|
|
+ {
|
|
|
+ if (_dictionaryRepresentation != DictionaryRepresentation.ArrayOfDocuments)
|
|
|
+ {
|
|
|
+ serializationInfo = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var serializer = new KeyValuePairSerializer<TKey, TValue>(
|
|
|
+ BsonType.Document,
|
|
|
+ _lazyKeySerializer.Value,
|
|
|
+ _lazyValueSerializer.Value);
|
|
|
+ serializationInfo = new BsonSerializationInfo(
|
|
|
+ null,
|
|
|
+ serializer,
|
|
|
+ serializer.ValueType);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo)
|
|
|
+ {
|
|
|
+ if (_dictionaryRepresentation != DictionaryRepresentation.Document)
|
|
|
+ {
|
|
|
+ serializationInfo = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ serializationInfo = new BsonSerializationInfo(
|
|
|
+ memberName,
|
|
|
+ _lazyValueSerializer.Value,
|
|
|
+ _lazyValueSerializer.Value.ValueType);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // protected methods
|
|
|
+ /// <summary>
|
|
|
+ /// Deserializes a value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">The deserialization context.</param>
|
|
|
+ /// <param name="args">The deserialization args.</param>
|
|
|
+ /// <returns>A deserialized value.</returns>
|
|
|
+ protected override TDictionary DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
|
|
|
+ {
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+
|
|
|
+ var bsonType = bsonReader.GetCurrentBsonType();
|
|
|
+ switch (bsonType)
|
|
|
+ {
|
|
|
+ case BsonType.Array:
|
|
|
+ return DeserializeArrayRepresentation(context);
|
|
|
+ case BsonType.Document:
|
|
|
+ return DeserializeDocumentRepresentation(context);
|
|
|
+ default:
|
|
|
+ throw CreateCannotDeserializeFromBsonTypeException(bsonType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Serializes a value.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="context">The serialization context.</param>
|
|
|
+ /// <param name="args">The serialization args.</param>
|
|
|
+ /// <param name="value">The object.</param>
|
|
|
+ protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+
|
|
|
+ switch (_dictionaryRepresentation)
|
|
|
+ {
|
|
|
+ case DictionaryRepresentation.Document:
|
|
|
+ SerializeDocumentRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case DictionaryRepresentation.ArrayOfArrays:
|
|
|
+ SerializeArrayOfArraysRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case DictionaryRepresentation.ArrayOfDocuments:
|
|
|
+ SerializeArrayOfDocumentsRepresentation(context, value);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ var message = string.Format("'{0}' is not a valid IDictionary<{1}, {2}> representation.",
|
|
|
+ _dictionaryRepresentation,
|
|
|
+ BsonUtils.GetFriendlyTypeName(typeof(TKey)),
|
|
|
+ BsonUtils.GetFriendlyTypeName(typeof(TValue)));
|
|
|
+ throw new BsonSerializationException(message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // protected methods
|
|
|
+ /// <summary>
|
|
|
+ /// Creates an accumulator.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The accumulator.</returns>
|
|
|
+ protected virtual ICollection<KeyValuePair<TKey, TValue>> CreateAccumulator()
|
|
|
+ {
|
|
|
+#pragma warning disable 618
|
|
|
+ return (ICollection<KeyValuePair<TKey, TValue>>)CreateInstance();
|
|
|
+#pragma warning restore 618
|
|
|
+ }
|
|
|
+
|
|
|
+ // protected methods
|
|
|
+ /// <summary>
|
|
|
+ /// Creates the instance.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The instance.</returns>
|
|
|
+ [Obsolete("CreateInstance is deprecated. Please use CreateAccumulator instead.")]
|
|
|
+ protected virtual TDictionary CreateInstance()
|
|
|
+ {
|
|
|
+ throw new NotImplementedException();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Finalizes an accumulator.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="accumulator">The accumulator to finalize</param>
|
|
|
+ /// <returns>The instance.</returns>
|
|
|
+ protected virtual TDictionary FinalizeAccumulator(ICollection<KeyValuePair<TKey, TValue>> accumulator)
|
|
|
+ {
|
|
|
+ return (TDictionary)accumulator;
|
|
|
+ }
|
|
|
+
|
|
|
+ // private methods
|
|
|
+ private TDictionary DeserializeArrayRepresentation(BsonDeserializationContext context)
|
|
|
+ {
|
|
|
+
|
|
|
+ var accumulator = CreateAccumulator();
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+ bsonReader.ReadStartArray();
|
|
|
+ while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
|
|
|
+ {
|
|
|
+ TKey key;
|
|
|
+ TValue value;
|
|
|
+
|
|
|
+ var bsonType = bsonReader.GetCurrentBsonType();
|
|
|
+ switch (bsonType)
|
|
|
+ {
|
|
|
+ case BsonType.Array:
|
|
|
+ bsonReader.ReadStartArray();
|
|
|
+ key = _lazyKeySerializer.Value.Deserialize(context);
|
|
|
+ value = _lazyValueSerializer.Value.Deserialize(context);
|
|
|
+ bsonReader.ReadEndArray();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case BsonType.Document:
|
|
|
+ key = default(TKey);
|
|
|
+ value = default(TValue);
|
|
|
+ _helper.DeserializeMembers(context, (elementName, flag) =>
|
|
|
+ {
|
|
|
+ switch (flag)
|
|
|
+ {
|
|
|
+ case Flags.Key: key = _lazyKeySerializer.Value.Deserialize(context); break;
|
|
|
+ case Flags.Value: value = _lazyValueSerializer.Value.Deserialize(context); break;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ throw CreateCannotDeserializeFromBsonTypeException(bsonType);
|
|
|
+ }
|
|
|
+
|
|
|
+ accumulator.Add(new KeyValuePair<TKey, TValue>(key, value));
|
|
|
+ }
|
|
|
+ bsonReader.ReadEndArray();
|
|
|
+
|
|
|
+ return FinalizeAccumulator(accumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ private TDictionary DeserializeDocumentRepresentation(BsonDeserializationContext context)
|
|
|
+ {
|
|
|
+ var accumulator = CreateAccumulator();
|
|
|
+
|
|
|
+ var bsonReader = context.Reader;
|
|
|
+ bsonReader.ReadStartDocument();
|
|
|
+ while (bsonReader.ReadBsonType() != BsonType.EndOfDocument)
|
|
|
+ {
|
|
|
+ var key = DeserializeKeyString(bsonReader.ReadName());
|
|
|
+ var value = _lazyValueSerializer.Value.Deserialize(context);
|
|
|
+ accumulator.Add(new KeyValuePair<TKey, TValue>(key, value));
|
|
|
+ }
|
|
|
+ bsonReader.ReadEndDocument();
|
|
|
+
|
|
|
+ return FinalizeAccumulator(accumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ private TKey DeserializeKeyString(string keyString)
|
|
|
+ {
|
|
|
+ var keyDocument = new BsonDocument("k", keyString);
|
|
|
+ using (var keyReader = new BsonDocumentReader(keyDocument))
|
|
|
+ {
|
|
|
+ var context = BsonDeserializationContext.CreateRoot(keyReader);
|
|
|
+ keyReader.ReadStartDocument();
|
|
|
+ keyReader.ReadName("k");
|
|
|
+ var key = _lazyKeySerializer.Value.Deserialize(context);
|
|
|
+ keyReader.ReadEndDocument();
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeArrayOfArraysRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ foreach (var keyValuePair in value)
|
|
|
+ {
|
|
|
+ if (keyValuePair.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ _lazyKeySerializer.Value.Serialize(context, keyValuePair.Key);
|
|
|
+ _lazyValueSerializer.Value.Serialize(context, keyValuePair.Value);
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeArrayOfDocumentsRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartArray();
|
|
|
+ foreach (var keyValuePair in value)
|
|
|
+ {
|
|
|
+ if (keyValuePair.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteStartDocument();
|
|
|
+ bsonWriter.WriteName("k");
|
|
|
+ _lazyKeySerializer.Value.Serialize(context, keyValuePair.Key);
|
|
|
+ bsonWriter.WriteName("v");
|
|
|
+ _lazyValueSerializer.Value.Serialize(context, keyValuePair.Value);
|
|
|
+ bsonWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SerializeDocumentRepresentation(BsonSerializationContext context, TDictionary value)
|
|
|
+ {
|
|
|
+ var bsonWriter = context.Writer;
|
|
|
+ bsonWriter.WriteStartDocument();
|
|
|
+ foreach (var keyValuePair in value)
|
|
|
+ {
|
|
|
+ if (keyValuePair.Value is not ISerializeToEntity)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bsonWriter.WriteName(SerializeKeyString(keyValuePair.Key));
|
|
|
+ _lazyValueSerializer.Value.Serialize(context, keyValuePair.Value);
|
|
|
+ }
|
|
|
+ bsonWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+
|
|
|
+ private string SerializeKeyString(TKey key)
|
|
|
+ {
|
|
|
+ var keyDocument = new BsonDocument();
|
|
|
+ using (var keyWriter = new BsonDocumentWriter(keyDocument))
|
|
|
+ {
|
|
|
+ var context = BsonSerializationContext.CreateRoot(keyWriter);
|
|
|
+ keyWriter.WriteStartDocument();
|
|
|
+ keyWriter.WriteName("k");
|
|
|
+ _lazyKeySerializer.Value.Serialize(context, key);
|
|
|
+ keyWriter.WriteEndDocument();
|
|
|
+ }
|
|
|
+
|
|
|
+ var keyValue = keyDocument["k"];
|
|
|
+ if (keyValue.BsonType != BsonType.String)
|
|
|
+ {
|
|
|
+ throw new BsonSerializationException("When using DictionaryRepresentation.Document key values must serialize as strings.");
|
|
|
+ }
|
|
|
+
|
|
|
+ return (string)keyValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // explicit interface implementations
|
|
|
+ IBsonSerializer IBsonDictionarySerializer.KeySerializer
|
|
|
+ {
|
|
|
+ get { return KeySerializer; }
|
|
|
+ }
|
|
|
+
|
|
|
+ IBsonSerializer IBsonDictionarySerializer.ValueSerializer
|
|
|
+ {
|
|
|
+ get { return ValueSerializer; }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|