asp.net core 3.1中对Mongodb BsonDocument的序列化和反序列化支持

最近项目使用了Mongodb作为非结构化数据的存储,大量使用了BsonDocument做为传输对象,但是面临一个问题,直接把BsonDocument通过web api返回前端时候,并不能序列化为期望的json;同样把json传输到后台也不能自动把json组装成BsonDocument

为此实现了一个自定义的asp.net core 3.1的JsonConverter来处理BsonDocument的序列化和反序列化。

首先定义一个BsonDocument的转换对象,并且实现JsonConverter<T>

public class BsonDocumentJsonConverter : JsonConverter<BsonDocument>
{
    /// <summary>
    /// 反序列化支持
    /// </summary>
    /// <param name="reader"></param>
    /// <param name="typeToConvert"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    public override BsonDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using (var jsonDoc = JsonDocument.ParseValue(ref reader))
        {
            using (var stream = new MemoryStream())
            {
                using (Utf8JsonWriter writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
                {
                    jsonDoc.WriteTo(writer);
                    writer.Flush();
                    string json = Encoding.UTF8.GetString(stream.ToArray());
                    return BsonDocument.Parse(json);
                }
            }
        }
    }
    /// <summary>
    /// 序列化支持
    /// </summary>
    /// <param name="writer"></param>
    /// <param name="value"></param>
    /// <param name="options"></param>
    public override void Write(Utf8JsonWriter writer, BsonDocument value, JsonSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteStringValue(String.Empty);
        }
        else
        {
            WirteJson(writer, value);
        }
    }
    private void WirteJson(Utf8JsonWriter writer, BsonDocument bson)
    {
        writer.WriteStartObject();
        var elements = bson.Elements;
        foreach (var item in elements)
        {
            WirteProperty(writer, item.Value, item.Name);
        }
        writer.WriteEndObject();
    }
    private void WirteProperty(Utf8JsonWriter writer, BsonValue bsonValue, string propertyName=null)
    {
        BsonType bsonType = bsonValue.BsonType;
        switch (bsonType)
        {
            case BsonType.EndOfDocument:
                break;
            case BsonType.Int32:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteNumberValue(bsonValue.AsInt32);
                }
                else
                {
                    writer.WriteNumber(propertyName, bsonValue.AsInt32);
                }
                break;
            case BsonType.Int64:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteNumberValue(bsonValue.AsInt64);
                }
                else
                {
                    writer.WriteNumber(propertyName, bsonValue.AsInt64);
                }
                break;
            case BsonType.Double:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteNumberValue(bsonValue.AsDouble);
                }
                else
                {
                    writer.WriteNumber(propertyName, bsonValue.AsDouble);
                }
                break;
            case BsonType.String:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteStringValue(bsonValue.AsString);
                }
                else
                {
                    writer.WriteString(propertyName, bsonValue.AsString);
                }
                break;
            case BsonType.Document:
                if (string.IsNullOrEmpty(propertyName))
                {
                    WirteJson(writer, bsonValue.AsBsonDocument);
                }
                else
                {
                    writer.WritePropertyName(propertyName);
                    WirteJson(writer, bsonValue.AsBsonDocument);
                }
                break;
            case BsonType.Array:
                if (string.IsNullOrEmpty(propertyName))
                {
                    var bsonArr = bsonValue.AsBsonArray;
                    writer.WriteStartArray();
                    foreach (var abson in bsonArr)
                    {
                        WirteProperty(writer, abson);
                    }
                    writer.WriteEndArray();
                }
                else
                {
                    var bsonArr = bsonValue.AsBsonArray;
                    writer.WritePropertyName(propertyName);
                    writer.WriteStartArray();
                    foreach (var abson in bsonArr)
                    {
                        WirteProperty(writer, abson);
                    }
                    writer.WriteEndArray();
                }
                break;
            case BsonType.Boolean:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteBooleanValue(bsonValue.AsBoolean);
                }
                else
                {
                    writer.WriteBoolean(propertyName, bsonValue.AsBoolean);
                }
                break;
            case BsonType.DateTime:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteStringValue(bsonValue.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz"));
                }
                else
                {
                    writer.WriteString(propertyName, bsonValue.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz"));
                }
                break;
            case BsonType.Null:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteNullValue();
                }
                else
                {
                    writer.WriteNull(propertyName);
                }
                break;
            case BsonType.ObjectId:
                if (string.IsNullOrEmpty(propertyName))
                {
                    writer.WriteStringValue(bsonValue.AsObjectId.ToString());
                }
                else
                {
                    writer.WriteString(propertyName, bsonValue.AsObjectId.ToString());
                }
                break;
            case BsonType.RegularExpression:
                break;
            case BsonType.JavaScript:
                break;
            case BsonType.Symbol:
                break;
            case BsonType.JavaScriptWithScope:
                break;
            case BsonType.Decimal128:
                break;
            case BsonType.MinKey:
                break;
            case BsonType.MaxKey:
                break;
            case BsonType.Timestamp:
                break;
            case BsonType.Binary:
                break;
            case BsonType.Undefined:
                break;
            default:
                break;
        }
    }
}

最后在Startup.cs文件中注册该转换器就好了

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddControllersWithViews().AddJsonOptions(option => {
        //....
        option.JsonSerializerOptions.Converters.Add(new BsonDocumentJsonConverter());
    });
}