从BsonDocument反序列化为字符串,再将其序列化回BsonDocument

3

我有一个需求,需要从MongoDB集合中获取一个实际为JSON值的属性,并将其反序列化为字符串。但是这种转换会抛出一个 "Cannot deserialize a 'String' from a BsonType 'Document'" 异常。

我尝试实现了一个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; }
}
1个回答

3
总之,您的模型上有一个公共的字符串属性,其中包含JSON,您希望通过将JSON字符串反序列化为一些中间 DTO,然后将DTO本身序列化到MongoDB中来内部序列化它。
以下是解决您问题的几种方法。
首先,您可以在数据模型中引入一个私有的DTO值属性 Dob SerializedDOB { get; set; },将该属性标记为 [BsonElement("dob")] 以强制进行序列化,然后修改 dob 为一个非序列化代理属性,在其getter和setter中从底层的 SerializedDOB 进行序列化和反序列化。以下代码显示了这种方法:
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字符串作为代理,设置程序自动确保其格式正确。
演示fiddle #1 在此处
其次,您可以为dob创建一个自定义{{link2:SerializerBase<string>}},在(反)序列化期间将字符串值映射到DTO Dob。以下代码显示了此方法:
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));
    }
}

这种方法的优点是可以在需要时重复使用JsonStringAsObjectSerializer<TObject>
演示fiddle #2 这里
以下扩展方法与两个解决方案一起使用,将JSON字符串反序列化为指定类型,因为令人困惑的是,BsonExtensionMethods有一个ToJson()方法,但没有FromJson()方法:
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);
        }
    }
}

有关解决方案的任何消息吗? - user1841787

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接