Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Partial work on CSHARP-311. Added new types BsonDictionaryOptionsAttr…
Browse files Browse the repository at this point in the history
…ibute, DictionaryRepresentation and DictionarySerializationOptions. Modified DictionaryGenericSerializer and DictionarySerializer to support new DictionarySerializationOptions and the new ArrayOfDocuments representation.
  • Loading branch information
rstam committed Sep 23, 2011
1 parent f0ee7a4 commit 134fb59
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 41 deletions.
2 changes: 2 additions & 0 deletions Bson/Bson.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,13 @@
<Compile Include="IO\JsonReaderSettings.cs" />
<Compile Include="ObjectModel\ICustomBsonTypeMapper.cs" />
<Compile Include="Serialization\Attributes\BsonDateTimeOptionsAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonDictionaryOptionsAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonExtraElementsAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonRepresentationAttribute.cs" />
<Compile Include="Serialization\Attributes\BsonSerializationOptionsAttribute.cs" />
<Compile Include="Serialization\Conventions\ExtraElementsMemberConventions.cs" />
<Compile Include="Serialization\Options\DateTimeSerializationOptions.cs" />
<Compile Include="Serialization\Options\DictionarySerializationOptions.cs" />
<Compile Include="Serialization\Options\DocumentSerializationOptions.cs" />
<Compile Include="Serialization\Options\RepresentationSerializationOptions.cs" />
<Compile Include="Exceptions\TruncationException.cs" />
Expand Down
71 changes: 71 additions & 0 deletions Bson/Serialization/Attributes/BsonDictionaryOptionsAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* Copyright 2010-2011 10gen 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.Generic;
using System.Linq;
using System.Text;

using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Options;

namespace MongoDB.Bson.Serialization.Attributes {
/// <summary>
/// Specifies serialization options for a Dictionary field or property.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonDictionaryOptionsAttribute : BsonSerializationOptionsAttribute {
#region private fields
private DictionaryRepresentation representation = DictionaryRepresentation.Dynamic;
#endregion

#region constructors
/// <summary>
/// Initializes a new instance of the BsonDictionaryOptionsAttribute class.
/// </summary>
public BsonDictionaryOptionsAttribute() {
}

/// <summary>
/// Initializes a new instance of the BsonDictionaryOptionsAttribute class.
/// </summary>
public BsonDictionaryOptionsAttribute(
DictionaryRepresentation representation
) {
this.representation = representation;
}
#endregion

#region public properties
/// <summary>
/// Gets or sets the external representation.
/// </summary>
public DictionaryRepresentation Representation {
get { return representation; }
set { representation = value; }
}
#endregion

#region public methods
/// <summary>
/// Gets the serialization options specified by this attribute.
/// </summary>
/// <returns>The serialization options.</returns>
public override IBsonSerializationOptions GetOptions() {
return new DictionarySerializationOptions(representation);
}
#endregion
}
}
125 changes: 125 additions & 0 deletions Bson/Serialization/Options/DictionarySerializationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* Copyright 2010-2011 10gen 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 MongoDB.Bson.IO;

namespace MongoDB.Bson.Serialization.Options {
/// <summary>
/// Represents the representation to use for dictionaries.
/// </summary>
public enum DictionaryRepresentation {
/// <summary>
/// Represent the dictionary as a document if the keys are strings and valid element names, otherwise as an array of arrays.
/// </summary>
Dynamic,
/// <summary>
/// Represent the dictionary as a Document.
/// </summary>
Document,
/// <summary>
/// Represent the dictionary as an array of arrays.
/// </summary>
ArrayOfArrays,
/// <summary>
/// Represent the dictionary as an array of documents.
/// </summary>
ArrayOfDocuments
}

/// <summary>
/// Represents serialization options for a Dictionary value.
/// </summary>
public class DictionarySerializationOptions : IBsonSerializationOptions {
#region private static fields
private static DictionarySerializationOptions defaults = new DictionarySerializationOptions();
private static DictionarySerializationOptions dynamic = new DictionarySerializationOptions(DictionaryRepresentation.Dynamic);
private static DictionarySerializationOptions document = new DictionarySerializationOptions(DictionaryRepresentation.Document);
private static DictionarySerializationOptions arrayOfArrays = new DictionarySerializationOptions(DictionaryRepresentation.ArrayOfArrays);
private static DictionarySerializationOptions arrayOfDocuments = new DictionarySerializationOptions(DictionaryRepresentation.ArrayOfDocuments);
#endregion

#region private fields
private DictionaryRepresentation representation = DictionaryRepresentation.Dynamic;
#endregion

#region constructors
/// <summary>
/// Initializes a new instance of the DictionarySerializationOptions class.
/// </summary>
public DictionarySerializationOptions() {
}

/// <summary>
/// Initializes a new instance of the DictionarySerializationOptions class.
/// </summary>
/// <param name="representation">The representation to use for a Dictionary.</param>
public DictionarySerializationOptions(
DictionaryRepresentation representation
) {
this.representation = representation;
}

#endregion

#region public static properties
/// <summary>
/// Gets an instance of DictionarySerializationOptions with Representation=ArrayOfArrays.
/// </summary>
public static DictionarySerializationOptions ArrayOfArrays {
get { return arrayOfArrays; }
}

/// <summary>
/// Gets an instance of DictionarySerializationOptions with Representation=ArrayOfDocuments.
/// </summary>
public static DictionarySerializationOptions ArrayOfDocuments {
get { return arrayOfDocuments; }
}

/// <summary>
/// Gets or sets the default Dictionary serialization options.
/// </summary>
public static DictionarySerializationOptions Defaults {
get { return defaults; }
set { defaults = value; }
}

/// <summary>
/// Gets an instance of DictionarySerializationOptions with Representation=Document.
/// </summary>
public static DictionarySerializationOptions Document {
get { return document; }
}

/// <summary>
/// Gets an instance of DictionarySerializationOptions with Representation=Dynamic.
/// </summary>
public static DictionarySerializationOptions Dynamic {
get { return dynamic; }
}
#endregion

#region public properties
/// <summary>
/// Gets the representation to use for a Dictionary.
/// </summary>
public DictionaryRepresentation Representation {
get { return representation; }
}
#endregion
}
}
112 changes: 91 additions & 21 deletions Bson/Serialization/Serializers/DictionaryGenericSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,55 @@ IBsonSerializationOptions options
var keyDiscriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(TKey));
var valueDiscriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(TValue));
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) {
bsonReader.ReadStartArray();
bsonReader.ReadBsonType();
var keyType = keyDiscriminatorConvention.GetActualType(bsonReader, typeof(TKey));
var keySerializer = BsonSerializer.LookupSerializer(keyType);
var key = (TKey) keySerializer.Deserialize(bsonReader, typeof(TKey), keyType, null);
bsonReader.ReadBsonType();
var valueType = valueDiscriminatorConvention.GetActualType(bsonReader, typeof(TValue));
var valueSerializer = BsonSerializer.LookupSerializer(valueType);
var value = (TValue) valueSerializer.Deserialize(bsonReader, typeof(TValue), valueType, null);
bsonReader.ReadEndArray();
dictionary.Add(key, value);
if (bsonReader.CurrentBsonType == BsonType.Array) {
bsonReader.ReadStartArray();
bsonReader.ReadBsonType();
var keyType = keyDiscriminatorConvention.GetActualType(bsonReader, typeof(TKey));
var keySerializer = BsonSerializer.LookupSerializer(keyType);
var key = (TKey) keySerializer.Deserialize(bsonReader, typeof(TKey), keyType, null);
bsonReader.ReadBsonType();
var valueType = valueDiscriminatorConvention.GetActualType(bsonReader, typeof(TValue));
var valueSerializer = BsonSerializer.LookupSerializer(valueType);
var value = (TValue) valueSerializer.Deserialize(bsonReader, typeof(TValue), valueType, null);
bsonReader.ReadEndArray();
dictionary.Add(key, value);
} else if (bsonReader.CurrentBsonType == BsonType.Document) {
bsonReader.ReadStartDocument();
TKey key = default(TKey);
TValue value = default(TValue);
bool keyFound = false, valueFound = false;
while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) {
var name = bsonReader.ReadName();
switch (name) {
case "k":
var keyType = keyDiscriminatorConvention.GetActualType(bsonReader, typeof(TKey));
var keySerializer = BsonSerializer.LookupSerializer(keyType);
key = (TKey) keySerializer.Deserialize(bsonReader, typeof(TKey), keyType, null);
keyFound = true;
break;
case "v":
var valueType = valueDiscriminatorConvention.GetActualType(bsonReader, typeof(TValue));
var valueSerializer = BsonSerializer.LookupSerializer(valueType);
value = (TValue) valueSerializer.Deserialize(bsonReader, typeof(TValue), valueType, null);
valueFound = true;
break;
default:
var message = string.Format("Element '{0}' is not valid for Dictionary items (expecting 'k' or 'v').", name);
throw new FileFormatException(message);
}
}
bsonReader.ReadEndDocument();
if (!keyFound) {
throw new FileFormatException("Dictionary item was missing the 'k' element.");
}
if (!valueFound) {
throw new FileFormatException("Dictionary item was missing the 'v' element.");
}
dictionary.Add(key, value);
} else {
var message = string.Format("Expected document or array for Dictionary item, not {0}.", bsonReader.CurrentBsonType);
throw new FileFormatException(message);
}
}
bsonReader.ReadEndArray();
return dictionary;
Expand Down Expand Up @@ -112,35 +150,55 @@ IBsonSerializationOptions options
} else {
var dictionary = (IDictionary<TKey, TValue>) value;

var representationOptions = options as RepresentationSerializationOptions;
BsonType representation;
if (representationOptions == null) {
var dictionaryOptions = options as DictionarySerializationOptions;
if (dictionaryOptions == null) {
// support RepresentationSerializationOptions for backward compatibility
var representationOptions = options as RepresentationSerializationOptions;
if (representationOptions != null) {
switch (representationOptions.Representation) {
case BsonType.Array:
dictionaryOptions = DictionarySerializationOptions.ArrayOfArrays;
break;
case BsonType.Document:
dictionaryOptions = DictionarySerializationOptions.Document;
break;
default:
var message = string.Format("BsonType {0} is not a valid representation for a Dictionary.", representationOptions.Representation);
throw new BsonSerializationException(message);
}
}

if (dictionaryOptions == null) {
dictionaryOptions = DictionarySerializationOptions.Defaults;
}
}

var representation = dictionaryOptions.Representation;
if (representation == DictionaryRepresentation.Dynamic) {
if (typeof(TKey) == typeof(string) || typeof(TKey) == typeof(object)) {
representation = BsonType.Document;
representation = DictionaryRepresentation.Document;
foreach (object key in dictionary.Keys) {
var name = key as string; // check for null and type string at the same time
if (name == null || name.StartsWith("$") || name.Contains(".")) {
representation = BsonType.Array;
representation = DictionaryRepresentation.ArrayOfArrays;
break;
}
}
} else {
representation = BsonType.Array;
representation = DictionaryRepresentation.ArrayOfArrays;
}
} else {
representation = representationOptions.Representation;
}

switch (representation) {
case BsonType.Document:
case DictionaryRepresentation.Document:
bsonWriter.WriteStartDocument();
foreach (KeyValuePair<TKey, TValue> entry in dictionary) {
bsonWriter.WriteName((string) (object) entry.Key);
BsonSerializer.Serialize(bsonWriter, typeof(TValue), entry.Value);
}
bsonWriter.WriteEndDocument();
break;
case BsonType.Array:
case DictionaryRepresentation.ArrayOfArrays:
bsonWriter.WriteStartArray();
foreach (KeyValuePair<TKey, TValue> entry in dictionary) {
bsonWriter.WriteStartArray();
Expand All @@ -150,6 +208,18 @@ IBsonSerializationOptions options
}
bsonWriter.WriteEndArray();
break;
case DictionaryRepresentation.ArrayOfDocuments:
bsonWriter.WriteStartArray();
foreach (KeyValuePair<TKey, TValue> entry in dictionary) {
bsonWriter.WriteStartDocument();
bsonWriter.WriteName("k");
BsonSerializer.Serialize(bsonWriter, typeof(TKey), entry.Key);
bsonWriter.WriteName("v");
BsonSerializer.Serialize(bsonWriter, typeof(TValue), entry.Value);
bsonWriter.WriteEndDocument();
}
bsonWriter.WriteEndArray();
break;
default:
var message = string.Format("'{0}' is not a valid representation for type IDictionary<{1}, {2}>.", representation, typeof(TKey).Name, typeof(TValue).Name);
throw new BsonSerializationException(message);
Expand Down
Loading

0 comments on commit 134fb59

Please sign in to comment.