c# - 从 BsonDocument 反序列化为字符串并序列化回 BsonDocument

标签 c# json mongodb serialization bson

我有一个要求,我需要一个属性,该属性实际上是来自 MongoDB 集合的 JSON 值,需要反序列化为字符串。此转换引发“无法从 BsonType 'Document' 反序列化 'String'”异常。

我尝试实现一个 JSON 自定义转换器,但由于该值被视为 BsonDocument,它没有帮助,我得到了同样的异常。我还需要原始格式,因为我需要将其转换回 BsonDocument 中。我想我需要一个自定义的 Bson 序列化器/反序列化器。

来自 MongoDB 集合的传入示例文档:

{
    "name": "Jane Doe",
    "dob": {
        "month": "Sep",
        "day": 09,
        "year": 1987
    }
}

键入它期望进行反序列化:
public class Person
{
    public string name { get; set; }
    public Dob dob { get; set; }

    public class Dob
    {
        public string month { get; set; }
        public int day { get; set; }
        public int year { get; set; }
    }
}

输入我希望它反序列化为:
public class Person
{
    public string name { get; set; }
    public string dob { get; set; }
}

最佳答案

总而言之,您的模型上有一个面向公众的 string 属性,其中包含 JSON,您希望通过将 JSON 字符串反序列化为某个中间 DTO 来内部序列化到 MongoDB,然后将 DTO 本身序列化到 Mongo。

以下是解决您的问题的几种方法。

首先 ,您可以将私有(private) DTO 值属性 Dob SerializedDOB { get; set; } 引入数据模型中,用 [BsonElement("dob")] 标记该属性以强制其被序列化,然后将 dob 修改为非序列化代理属性,该代理属性从底层 SerializedDOB 序列化到底层 SerializerBase<string>在它的 getter 和 setter 中。以下代码显示了这种方法:

public class Person
{
    public string name { get; set; }

    [BsonIgnore]
    public string dob
    {
        get => BsonExtensionMethods.ToJson(SerializedDOB);
        set => SerializedDOB = MyBsonExtensionMethods.FromJson<Dob>(value);
    }

    [BsonElement("dob")]
    Dob SerializedDOB { get; set; }

    class Dob // The DTO
    {
        public string month { get; set; }
        public int day { get; set; }
        public int year { get; set; }
    }
}

这种方法的优点是,通过使 JSON 字符串成为代理项,setter 会自动确保其格式正确。

演示 fiddle #1 here

其次 ,您可以为 dob 创建自定义 Dob ,在(反)序列化期间将字符串值映射到 DTO JsonStringAsObjectSerializer<TObject> 和从 DTO BsonExtensionMethods 映射。以下代码显示了这种方法:
public class Person
{
    public string name { get; set; }

    [BsonSerializer(typeof(JsonStringAsObjectSerializer<Dob>))]
    public string dob { get; set; }

    class Dob // The DTO
    {
        public string month { get; set; }
        public int day { get; set; }
        public int year { get; set; }
    }
}

public class JsonStringAsObjectSerializer<TObject> : SerializerBase<string> where TObject : class
{
    public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, string value)
    {
        if (value == null)
        {
            var bsonWriter = context.Writer;
            bsonWriter.WriteNull();
        }
        else
        {
            var obj = MyBsonExtensionMethods.FromJson<TObject>(value);
            var serializer = BsonSerializer.LookupSerializer(typeof(TObject));
            serializer.Serialize(context, obj);
        }           
    }

    public override string Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
    {
        var bsonReader = context.Reader;
        var serializer = BsonSerializer.LookupSerializer(typeof(TObject));
        var obj = (TObject)serializer.Deserialize(context);
        return (obj == null ? null : BsonExtensionMethods.ToJson(obj));
    }
}

这种方法的优点是,只要出现这种需求,就可以重用 ToJson()

演示 fiddle #2 here

以下扩展方法与这两种解决方案一起用于将 JSON 字符串反序列化为指定类型,因为令人困惑的是, FromJson() 有一个 ojit_code 方法但没有 ojit_code 方法:
public static partial class MyBsonExtensionMethods
{
    // Not sure why but BsonExtensionMethods.cs seems to lack methods for deserializing from JSON, so I added some here.
    // See https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Bson/BsonExtensionMethods.cs 

    public static TNominalType FromJson<TNominalType>(
        string json,
        JsonReaderSettings readerSettings = null,
        IBsonSerializer<TNominalType> serializer = null,
        Action<BsonDeserializationContext.Builder> configurator = null)
    {
        return (TNominalType)FromJson(json, typeof(TNominalType), readerSettings, serializer, configurator);
    }

    public static object FromJson(
        string json,
        Type nominalType,
        JsonReaderSettings readerSettings = null,
        IBsonSerializer serializer = null,
        Action<BsonDeserializationContext.Builder> configurator = null)
    {
        if (nominalType == null || json == null)
            throw new ArgumentNullException();
        serializer = serializer ?? BsonSerializer.LookupSerializer(nominalType);
        if (serializer.ValueType != nominalType)
            throw new ArgumentException(string.Format("serializer.ValueType {0} != nominalType {1}.", serializer.GetType().FullName, nominalType.FullName), "serializer");

        using (var textReader = new StringReader(json)) 
        using (var reader = new JsonReader(textReader, readerSettings ?? JsonReaderSettings.Defaults))
        {
            var context = BsonDeserializationContext.CreateRoot(reader, configurator);
            return serializer.Deserialize(context);
        }
    }
}

关于c# - 从 BsonDocument 反序列化为字符串并序列化回 BsonDocument,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59441376/

相关文章:

c# - 将 x86 平台更改为 Any CPU for .NET 3.5 抛出 COMException

javascript - 在 Json 中搜索并获得有限的搜索结果

mongodb - mongo shell 的同步/异步属性是什么?

node.js - 在单个 mongoose 命令中更新多个文档

c# - 如何从 C# 保存到 Excel?

c# - "[]"是有效的 JSON 吗?

javascript - 无法将 json 数据传递给 Ajax 函数

javascript - EditableGrid 无法加载内联 json 数据

java - Spring MongoDB中批量存在查询/存在多个id

c# - MongoDb 和自引用对象