使用 Mongo Bson 序列化,如何使用具有非标准鉴别器字段的多态数据模式创建适当的反序列化器

问题描述 投票:0回答:1

给定一个文件

{
   "shape": {
      "_bsonName": "[DiscriminatorValue]",
      "geoJson": ...
   }
}

[DiscriminatorValue]
可以是以下之一:

  • “公司::制图::地理围栏::GeoEllipse”
  • “公司::映射::地理围栏::GeoRectangle”
  • “公司::绘图::地理围栏::GeoPolygon”

每种不同的类型在 json 中都有自己的数据形状

基本上,mongo 通常用于鉴别器的 _t 字段需要是“_bsonName”,但我还必须读取 _bsonName 字段并返回我们的 C# 类型:

RectangleShape|EllipseShape|PolygonShape

所以我创建了一个 IDiscriminatorConvention 类

public class GeofenceShapeDiscriminatorConvention : IDiscriminatorConvention
{
    public string ElementName => "_bsonName";

    public Type GetActualType(IBsonReader bsonReader, Type nominalType)
    {
        var bookmark = bsonReader.GetBookmark();

        string bsonName = null;
        bsonReader.ReadStartDocument();
        if(bsonReader.FindElement("_bsonName"))
        {
            bsonName = bsonReader.ReadString();
        }

        bsonReader.ReturnToBookmark(bookmark);

        switch (bsonName)
        {
            case "Company::Mapping::Geofences::GeoEllipse":
                return typeof(EllipseGeofenceShape);
            case "Company::Mapping::Geofences::GeoRectangle":
                return typeof(RectangleGeofenceShape);
            case "Company::Mapping::Geofences::GeoPolygon":
                return typeof(PolygonGeofenceShape);
            default:
                throw new NotSupportedException($"Unexpected shape._bsonName of {bsonName}.");
        }
    }

    public BsonValue GetDiscriminator(Type nominalType, Type actualType)
    {
        switch(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";
            default:
                throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
        }
    }
}

不幸的是,此代码失败并出现 StackOverflowException,其中 Mongo 代码不断一遍又一遍地调用 GetActualType。

查看了他们的源代码,没有很多合适的例子。

值得注意的是,如果我跳过

bsonReader.ReturnToBookmark(bookmark);
,那么我会得到一个不同的错误,基本上是
failed to GetBsonType() on a node of type value
(我忘记了确切的措辞)

mongodb mongodb-.net-driver
1个回答
0
投票

您可以从

StandardDiscriminatorConvention
派生并使用
BsonDiscriminator
属性,而不是从头开始编写鉴别器约定,例如:

internal class GeofenceDiscriminatorConvention : StandardDiscriminatorConvention
{
    public GeofenceDiscriminatorConvention() : base("_bsonName")
    {
    }

    public static void Register()
    {
        BsonSerializer.RegisterDiscriminatorConvention(typeof(GeofenceShape), new GeofenceDiscriminatorConvention());
    }

    public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
    {
        switch (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";
            default:
                throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
        }
    }
}

GeofenceShape
类上,您可以像这样设置相应的鉴别器值:

// Base class
[BsonKnownTypes(typeof(EllipseGeofenceShape), typeof(PolygonGeofenceShape), typeof(RectangleGeofenceShape))]
internal class GeofenceShape
{
    [BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
    public string Id { get; set; }
}

// Derived class
[BsonDiscriminator("Company::Mapping::Geofences::GeoPolygon")]
internal class PolygonGeofenceShape : GeofenceShape
{
}
© www.soinside.com 2019 - 2024. All rights reserved.