Skip to content
Browse files

Merged JornWildt's TypeConverter code.

  • Loading branch information...
1 parent 698c298 commit 60723272c838f948a266dd1dc90632a3cd1e28b8 @atheken committed Aug 3, 2010
View
33 NoRM.Tests/BSONSerializerTest.cs
@@ -5,6 +5,8 @@
using Xunit;
using Norm.BSON;
using System.Linq;
+using System.Globalization;
+using Norm.Configuration;
namespace Norm.Tests
{
@@ -467,6 +469,35 @@ public void SerializesExtraPropertiesToExpandoCollection()
Assert.Equal(expando["State"], address.State);
Assert.Equal(expando["Zip"], address.Zip);
}
-
+
+ [Fact]
+ public void SerializesCultureInfo()
+ {
+ var s1 = new CultureInfoDTO() { Culture = CultureInfo.GetCultureInfo("en-US") };
+ var bytes = BsonSerializer.Serialize(s1);
+ var s2 = BsonDeserializer.Deserialize<CultureInfoDTO>(bytes);
+ Assert.Equal(s1.Culture, s2.Culture);
+ }
+
+ [Fact]
+ public void SerializesClassWithCustomValueObjectUsingCustomTypeConverter()
+ {
+ IMongoConfigurationMap cfg = new MongoConfigurationMap();
+ cfg.TypeConverterFor<NonSerializableValueObject, NonSerializableValueObjectTypeConverter>();
+ BsonSerializer.UseConfiguration(cfg);
+
+ // Verify that a contained, normally unserializable, value can be serialized with a proper type converter
+ var s1 = new NonSerializableClass()
+ {
+ Value = new NonSerializableValueObject("12345"),
+ Text = "Abc"
+ };
+ var bytes = BsonSerializer.Serialize(s1);
+ var s2 = BsonDeserializer.Deserialize<NonSerializableClass>(bytes);
+ Assert.Equal(s1.Value.Number, s2.Value.Number);
+ Assert.Equal(s1.Text, s2.Text);
+
+ BsonSerializer.UseConfiguration(null);
+ }
}
}
View
23 NoRM.Tests/MongoConfigurationTests.cs
@@ -17,6 +17,7 @@ public MongoConfigurationTests()
MongoConfiguration.RemoveMapFor<Shopper>();
MongoConfiguration.RemoveMapFor<Cart>();
MongoConfiguration.RemoveMapFor<TestProduct>();
+ MongoConfiguration.RemoveTypeConverterFor<NonSerializableValueObject>();
using (var admin = new MongoAdmin(TestHelper.ConnectionString()))
{
@@ -202,7 +203,7 @@ public void Are_Queries_Fully_Linqified()
}
});
- var deepQuery = shoppers.Where(x => x.Cart.CartSuppliers.Any(y=>y.Name == "Supplier1")).ToList();
+ var deepQuery = shoppers.Where(x => x.Cart.CartSuppliers.Any(y => y.Name == "Supplier1")).ToList();
Assert.Equal("John", deepQuery[0].Name);
Assert.Equal("Cart1", deepQuery[0].Cart.Name);
Assert.Equal(1, deepQuery.Count);
@@ -255,7 +256,7 @@ public void Subclass_Adheres_To_Superclass_Fluent_Alias()
var found = mongo.GetCollection<SuperClassObjectFluentMapped>("Fake").Find();
Assert.Equal(2, found.Count());
- Assert.Equal(obj1.Id, found.ElementAt(0).Id) ;
+ Assert.Equal(obj1.Id, found.ElementAt(0).Id);
Assert.Equal("Prod1", found.ElementAt(0).Title);
Assert.Equal(obj2.Id, found.ElementAt(1).Id);
Assert.Equal("Prod2", found.ElementAt(1).Title);
@@ -276,21 +277,13 @@ public void Subclassed_Type_Is_Returned_When_Superclass_Is_Used_For_The_Collecti
Assert.Equal(typeof(SubClassedObjectFluentMapped), found.ElementAt(0).GetType());
}
}
-
+
[Fact]
- public void Can_fluently_configure_discriminator_for_all_implementations_of_an_interface()
+ public void Can_Register_TypeConverter()
{
- MongoConfiguration.Initialize(r => r.AddMap<DiscriminationMap>());
-
- using (var mongo = Mongo.Create(TestHelper.ConnectionString()))
- {
- var obj1 = new InterfacePropertyContainingClass();
- mongo.GetCollection<InterfacePropertyContainingClass>().Insert(obj1);
- var found = mongo.GetCollection<InterfacePropertyContainingClass>().Find();
-
- Assert.Equal(1, found.Count());
- Assert.Equal(typeof(NotDiscriminatedClass), found.ElementAt(0).InterfaceProperty.GetType());
- }
+ MongoConfiguration.Initialize(c => c.TypeConverterFor<NonSerializableValueObject, NonSerializableValueObjectTypeConverter>());
+ IBsonTypeConverter converter = MongoConfiguration.ConfigurationContainer.GetTypeConverterFor(typeof(NonSerializableValueObject));
+ Assert.Equal(typeof(NonSerializableValueObjectTypeConverter), converter.GetType());
}
}
}
View
53 NoRM.Tests/TestClasses.cs
@@ -12,6 +12,7 @@
using System.ComponentModel;
using Norm.BSON;
using System.Collections;
+using System.Globalization;
namespace Norm.Tests
{
@@ -125,12 +126,15 @@ public IQueryable<Post> Posts
get { return _provider.GetCollection<Post>().AsQueryable(); }
}
+ #region IDisposable Members
public void Dispose()
{
_provider.Dispose();
}
+ #endregion
+
public T MapReduce<T>(string map, string reduce)
{
T result = default(T);
@@ -532,6 +536,55 @@ public class ChildGeneralDTO : GeneralDTO
public bool IsOver9000 { get; set; }
}
+ public class CultureInfoDTO
+ {
+ public CultureInfo Culture { get; set; }
+ }
+
+ public class NonSerializableClass
+ {
+ public NonSerializableValueObject Value { get; set; }
+ public string Text { get; set; }
+ }
+
+ public class NonSerializableValueObject
+ {
+ // Stuff a few properties in here that Norm normally cannot handle
+ private ArgumentException ex { get; set; }
+ private NonSerializableValueObject MakeNormCrashReference { get; set; }
+
+ public string Number { get; private set; }
+
+ public NonSerializableValueObject(string number)
+ {
+ Number = number;
+ MakeNormCrashReference = this;
+ }
+ }
+
+ public class NonSerializableValueObjectTypeConverter : IBsonTypeConverter
+ {
+ #region IBsonTypeConverter Members
+
+ public Type SerializedType
+ {
+ get { return typeof(string); }
+ }
+
+ public object ConvertToBson(object data)
+ {
+ return ((NonSerializableValueObject)data).Number;
+ }
+
+ public object ConvertFromBson(object data)
+ {
+ return new NonSerializableValueObject((string)data);
+ }
+
+ #endregion
+ }
+
+
[MongoDiscriminated]
public class SuperClassObject
{
View
10 NoRM/BSON/BSONSerializer.cs
@@ -7,12 +7,13 @@
using Norm.BSON.DbTypes;
using Norm.Configuration;
+
namespace Norm.BSON
{
/// <summary>
/// The bson serializer.
/// </summary>
- internal class BsonSerializer
+ internal class BsonSerializer : BsonSerializerBase
{
private static readonly IDictionary<Type, BSONTypes> _typeMap = new Dictionary<Type, BSONTypes>
{
@@ -212,6 +213,13 @@ private void SerializeMember(string name, object value)
}
var type = value.GetType();
+ IBsonTypeConverter converter = Configuration.GetTypeConverterFor(type);
+ if (converter != null)
+ {
+ value = converter.ConvertToBson(value);
+ }
+
+ type = value.GetType();
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
View
44 NoRM/BSON/BsonDeserializer.cs
@@ -1,17 +1,18 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using Norm.Configuration;
+
+
namespace Norm.BSON
{
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using System.Text;
- using System.Text.RegularExpressions;
-
/// <summary>
/// BSON Deserializer
/// </summary>
- public class BsonDeserializer
+ public class BsonDeserializer : BsonSerializerBase
{
private static readonly Type _IEnumerableType = typeof(IEnumerable);
private static readonly Type _IDictionaryType = typeof(IDictionary<,>);
@@ -163,14 +164,37 @@ private object DeserializeValue(Type type, BSONTypes storedType)
}
/// <summary>
- /// Deserializes the value.
+ /// Applies optional type conversion and deserializes the value.
/// </summary>
/// <param retval="type">The type.</param>
/// <param retval="storedType">Type of the stored.</param>
/// <param retval="container">The container.</param>
/// <returns></returns>
private object DeserializeValue(Type type, BSONTypes storedType, object container)
{
+ IBsonTypeConverter converter = Configuration.GetTypeConverterFor(type);
+ if (converter != null)
+ {
+ Type serializedType = converter.SerializedType;
+ object value = DeserializeValueAfterConversion(serializedType, storedType, container);
+ return converter.ConvertFromBson(value);
+ }
+ else
+ {
+ return DeserializeValueAfterConversion(type, storedType, container);
+ }
+ }
+
+
+ /// <summary>
+ /// Deserializes the value after any type conversion has been applied.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="storedType">Type of the stored.</param>
+ /// <param name="container">The container.</param>
+ /// <returns></returns>
+ private object DeserializeValueAfterConversion(Type type, BSONTypes storedType, object container)
+ {
if (storedType == BSONTypes.Null)
{
return null;
View
34 NoRM/BSON/BsonSerializerBase.cs
@@ -0,0 +1,34 @@
+using Norm.Configuration;
+
+
+namespace Norm.BSON
+{
+ public class BsonSerializerBase
+ {
+ private static IMongoConfigurationMap _configuration;
+ protected static IMongoConfigurationMap Configuration
+ {
+ get
+ {
+ if (_configuration != null)
+ return _configuration;
+ else
+ return MongoConfiguration.ConfigurationContainer;
+ }
+ set
+ {
+ _configuration = value;
+ }
+ }
+
+ /// <summary>
+ /// Use a specific configuration instance instead of the default.
+ /// </summary>
+ /// <remarks>This is by no way thread safe and is only intended for use in the internal automated tests.</remarks>
+ /// <param name="config"></param>
+ public static void UseConfiguration(IMongoConfigurationMap config)
+ {
+ Configuration = config;
+ }
+ }
+}
View
12 NoRM/BSON/IBsonTypeConverter.cs
@@ -0,0 +1,12 @@
+using System;
+
+
+namespace Norm.BSON
+{
+ public interface IBsonTypeConverter
+ {
+ Type SerializedType { get; }
+ object ConvertToBson(object data);
+ object ConvertFromBson(object data);
+ }
+}
View
28 NoRM/BSON/TypeConverters/CultureInfoTypeConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Globalization;
+
+
+namespace Norm.BSON.TypeConverters
+{
+ public class CultureInfoTypeConverter : IBsonTypeConverter
+ {
+ #region IBsonTypeConverter Members
+
+ public Type SerializedType
+ {
+ get { return typeof(string); }
+ }
+
+ public object ConvertToBson(object data)
+ {
+ return ((CultureInfo)data).Name;
+ }
+
+ public object ConvertFromBson(object data)
+ {
+ return new CultureInfo((string)data);
+ }
+
+ #endregion
+ }
+}
View
7 NoRM/Configuration/ConfigurationContainer.cs
@@ -1,11 +1,18 @@
+using System.Globalization;
+using Norm.BSON.TypeConverters;
namespace Norm.Configuration
{
/// <summary>
/// Mongo configuration container
/// </summary>
public class ConfigurationContainer : MongoConfigurationMap, IConfigurationContainer
{
+ public ConfigurationContainer()
+ {
+ TypeConverterFor<CultureInfo, CultureInfoTypeConverter>();
+ }
+
/// <summary>
/// Registers a Mongo Configuration Map by calling the default
/// constructor of T (so that's where you should add your mapping logic)
View
8 NoRM/Configuration/IMongoConfigurationMap.cs
@@ -1,4 +1,6 @@
using System;
+using Norm.BSON;
+
namespace Norm.Configuration
{
@@ -22,6 +24,12 @@ public interface IMongoConfigurationMap : IHideObjectMembers
/// <typeparam retval="T">The type for which to remove fluent mappings.</typeparam>
void RemoveFor<T>();
+ void TypeConverterFor<TClr, TCnv>() where TCnv : IBsonTypeConverter, new();
+
+ IBsonTypeConverter GetTypeConverterFor(Type t);
+
+ void RemoveTypeConverterFor<TClr>();
+
/// <summary>
/// Gets the retval of the type's collection.
/// </summary>
View
18 NoRM/Configuration/MongoConfiguration.cs
@@ -56,6 +56,19 @@ public static void RemoveMapFor<T>()
}
/// <summary>
+ /// Remove a type converter for the specified type.
+ /// </summary>
+ /// <remarks>This is here for unit testing support, use at your own risk.</remarks>
+ /// <typeparam name="TClr"></typeparam>
+ public static void RemoveTypeConverterFor<TClr>()
+ {
+ if (_configuration != null)
+ {
+ _configuration.RemoveTypeConverterFor<TClr>();
+ }
+ }
+
+ /// <summary>
/// Allows various objects to fire type change event.
/// </summary>
/// <param retval="t"></param>
@@ -90,6 +103,11 @@ internal static string GetPropertyAlias(Type type, string propertyName)
return _configuration != null ? _configuration.GetConfigurationMap().GetPropertyAlias(type, propertyName) : propertyName;
}
+ internal static IBsonTypeConverter GetBsonTypeConverter(Type t)
+ {
+ return _configuration != null ? _configuration.GetTypeConverterFor(t) : null;
+ }
+
/// <summary>
/// Given the type, get the fluently configured collection type.
/// </summary>
View
35 NoRM/Configuration/MongoConfigurationMap.cs
@@ -15,6 +15,9 @@ public class MongoConfigurationMap : IMongoConfigurationMap
{
private Dictionary<Type, String> _idProperties = new Dictionary<Type, string>();
+ private IDictionary<Type, IBsonTypeConverter> TypeConverters = new Dictionary<Type, IBsonTypeConverter>();
+ //private IBsonTypeConverterRegistry BsonTypeConverterRegistry = new BsonTypeConverterRegistry();
+
/// <summary>
/// Configures properties for type T
/// </summary>
@@ -26,6 +29,38 @@ public void For<T>(Action<ITypeConfiguration<T>> typeConfigurationAction)
typeConfigurationAction((ITypeConfiguration<T>)typeConfiguration);
}
+ /// <summary>
+ /// Configures a type converter for type TClr
+ /// </summary>
+ /// <remarks>A type converter is used to convert any .NET CLR type into a CLR type that Mongo BSON
+ /// understands. For instance turning a CultureInfo into a string and back again. This method registers
+ /// known converters.</remarks>
+ /// <typeparam name="TClr"></typeparam>
+ /// <typeparam name="TCnv"></typeparam>
+ public void TypeConverterFor<TClr, TCnv>() where TCnv : IBsonTypeConverter, new()
+ {
+ Type ClrType = typeof(TClr);
+ Type CnvType = typeof(TCnv);
+
+ if (TypeConverters.ContainsKey(ClrType))
+ throw new ArgumentException(string.Format("The type '{0}' has already a type converter registered ({1}). You are trying to register '{2}'",
+ ClrType, TypeConverters[ClrType], CnvType));
+
+ TypeConverters.Add(ClrType, new TCnv());
+ }
+
+ public IBsonTypeConverter GetTypeConverterFor(Type t)
+ {
+ IBsonTypeConverter converter = null;
+ TypeConverters.TryGetValue(t, out converter);
+ return converter;
+ }
+
+ public void RemoveTypeConverterFor<TClr>()
+ {
+ TypeConverters.Remove(typeof(TClr));
+ }
+
private bool IsIdPropertyForType(Type type, String propertyName)
{
bool retval = false;
View
3 NoRM/NoRM.csproj
@@ -79,6 +79,7 @@
<Compile Include="Attributes\MongoIgnoreIfNullAttribute.cs" />
<Compile Include="BSON\BsonDeserializer.cs" />
<Compile Include="BSON\BsonHelper.cs" />
+ <Compile Include="BSON\BsonSerializerBase.cs" />
<Compile Include="BSON\Command.cs" />
<Compile Include="BSON\DbTypes\GridFile.cs" />
<Compile Include="BSON\DbTypes\ObjectId.cs" />
@@ -88,6 +89,7 @@
<Compile Include="BSON\BsonSerializer.cs" />
<Compile Include="BSON\BSONTypes.cs" />
<Compile Include="BSON\ExpandoProperty.cs" />
+ <Compile Include="BSON\IBsonTypeConverter.cs" />
<Compile Include="BSON\IdPropertyFinder.cs" />
<Compile Include="BSON\IExpando.cs" />
<Compile Include="BSON\Lists\ArrayWrapper.cs" />
@@ -104,6 +106,7 @@
<Compile Include="BSON\DocumentExceedsSizeLimitsException.cs" />
<Compile Include="BSON\ModifierCommand.cs" />
<Compile Include="BSON\ReflectionHelper.cs" />
+ <Compile Include="BSON\TypeConverters\CultureInfoTypeConverter.cs" />
<Compile Include="Collections\CreateCollectionOptions.cs" />
<Compile Include="Collections\IMongoCollectionGeneric.cs" />
<Compile Include="Collections\MongoCollection.cs" />

0 comments on commit 6072327

Please sign in to comment.
Something went wrong with that request. Please try again.