using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.Conventions;
public static void Main()
string geofenceBsonStr1 = @"{ ""_id"": ObjectId(""65aaae00f65234cca4345200""), ""_bsonName"":""Company::Mapping::Geofence"", ""shape"": { ""_bsonName"": ""Company::Mapping::Geofence::GeoRectangle"", ""bottom"": 1, ""left"": 2, ""right"": 3, ""top"": 4 } }";
string geofenceBsonStr2 = @"{ ""_id"": ObjectId(""65aaae00f65234cca4345201""), ""shape"": { ""_bsonName"": ""Company::Mapping::Geofence::GeoPolygon"", ""points"": [ [0,0], [1,0], [1,1], [0,1] ] } }";
BsonSerializer.RegisterDiscriminatorConvention(typeof(GeofenceShape), new Markus_GeofenceDiscriminatorConvention());
var geofence1 = BsonSerializer.Deserialize<Geofence>(geofenceBsonStr1);
var rectangle = (RectangleGeofenceShape)geofence1.Shape;
rectangle.Should().BeEquivalentTo(new RectangleGeofenceShape() { Bottom=1, Left=2, Right=3, Top=4 });
Console.WriteLine(ObjectDumper.Dump(geofence1));
var geofence2 = BsonSerializer.Deserialize<Geofence>(geofenceBsonStr2);
var polygon = (PolygonGeofenceShape)geofence2.Shape;
Console.WriteLine(ObjectDumper.Dump(geofence2));
public ObjectId Id { get; set; }
[BsonElement("_bsonName")]
public string BsonName { get; set; }
public GeofenceShape Shape { get; set; }
[BsonKnownTypes(typeof(RectangleGeofenceShape), typeof(PolygonGeofenceShape), typeof(EllipseGeofenceShape))]
public abstract class GeofenceShape
[BsonElement("_bsonName")]
public string BsonName { get; set; }
[BsonDiscriminator("Company::Mapping::Geofence::GeoRectangle")]
public class RectangleGeofenceShape : GeofenceShape
public RectangleGeofenceShape() { BsonName = "Company::Mapping::Geofence::GeoRectangle"; }
[BsonElement("bottom")] public decimal Bottom { get; set; }
[BsonElement("left")] public decimal Left { get; set; }
[BsonElement("right")] public decimal Right { get; set; }
[BsonElement("top")] public decimal Top { get; set; }
[BsonDiscriminator("Company::Mapping::Geofence::GeoPolygon")]
public class PolygonGeofenceShape : GeofenceShape
public PolygonGeofenceShape() { BsonName = "Company::Mapping::Geofence::GeoPolygon"; }
[BsonElement("points")] public decimal[][] Points { get; set; }
[BsonDiscriminator("Company::Mapping::Geofence::GeoEllipse")]
public class EllipseGeofenceShape : GeofenceShape
public EllipseGeofenceShape() { BsonName = "Company::Mapping::Geofence::GeoEllipse"; }
internal class Markus_GeofenceDiscriminatorConvention : StandardDiscriminatorConvention
public Markus_GeofenceDiscriminatorConvention() : base("_bsonName")
public static void Register()
BsonSerializer.RegisterDiscriminatorConvention(typeof(GeofenceShape), new Markus_GeofenceDiscriminatorConvention());
public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
case Type _ when actualType == typeof(EllipseGeofenceShape):
return "Company::Mapping::Geofence::GeoEllipse";
case Type _ when actualType == typeof(RectangleGeofenceShape):
return "Company::Mapping::Geofence::GeoRectangle";
case Type _ when actualType == typeof(PolygonGeofenceShape):
return "Company::Mapping::Geofence::GeoPolygon";
throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
public class GeofenceShapeDiscriminatorConvention : IDiscriminatorConvention
public string ElementName => "_bsonName";
public Type GetActualType(IBsonReader bsonReader, Type nominalType)
var bookmark = bsonReader.GetBookmark();
bsonReader.ReadStartDocument();
if (!bsonReader.FindElement("_bsonName"))
throw new NotSupportedException($"Could not find element named: {ElementName}");
string typeName = bsonReader.ReadString();
case "Company::Mapping::Geofences::GeoEllipse":
return typeof(EllipseGeofenceShape);
case "Company::Mapping::Geofences::GeoRectangle":
return typeof(RectangleGeofenceShape);
case "Company::Mapping::Geofences::GeoPolygon":
return typeof(PolygonGeofenceShape);
throw new NotSupportedException($"Could not find a type to match: {typeName}.");
bsonReader.ReturnToBookmark(bookmark);
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
case Type _ when actualType == typeof(EllipseGeofenceShape):
return "Company::Mapping::Geofences::GeoEllipse";
case Type _ when actualType == typeof(RectangleGeofenceShape):
return "Company::Mapping::Geofences::GeoRectangle";
case Type _ when actualType == typeof(PolygonGeofenceShape):
return "Company::Mapping::Geofences::GeoPolygon";
throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
public static class EzBson
public static T Deserialize<T>(string bsonDocument)
throw new NotImplementedException();
public static string Combine(params string[] bsonDocuments)
var sb = new System.Text.StringBuilder();
foreach(var bsonStr in bsonDocuments) { if(sb.Length>0) sb.Append(", "); sb.Append(bsonStr); }
return "[" + sb.ToString() + "]";