[英]MongoDB Custom Serializer to avoid _t being added collection, throws ReadEndArray Error?
Situation: Language: C# using the C# Driver I have a model that contains a List as a property.情况:语言:使用 C# 驱动程序的 C# 我有一个模型,其中包含一个 List 作为属性。 That List can contain one of 3 different models that all inherit the BaseModelClass.该列表可以包含全部继承 BaseModelClass 的 3 个不同模型之一。 To assist in serialization of this situation Mongo adds _t to identify which of the models is actually being used.为了帮助序列化这种情况,Mongo 添加了 _t 来识别实际使用的模型。 For us this is a problem due to the amount of space that _t is taking up.对我们来说,这是一个问题,因为 _t 占用的空间量很大。 I am a lowly dev, I have asked for more space and ram and they have told me to solve it without the additional space.我是一个低级的开发人员,我要求更多的空间和内存,他们告诉我在没有额外空间的情况下解决它。 So I sat down to writing a custom serializer that handled the different types without writing a _t to the BSONDocument.所以我坐下来编写一个处理不同类型的自定义序列化程序,而无需将 _t 写入 BSONDocument。 I thought all was great until I started doing my unit testing of the serialization.在我开始对序列化进行单元测试之前,我认为一切都很好。 I started getting "ReadEndArray can only be called when ContextType is Array, not when ContextType is Document."我开始收到“只能在 ContextType 为 Array 时调用 ReadEndArray,而不能在 ContextType 为 Document 时调用。”
Any advice or suggestions are greatly appreciated.非常感谢任何意见或建议。
Here is the code I have thus far...这是我到目前为止的代码......
<---------Collection Model---------------------> <---------收藏模型--------->
[BsonCollectionName("calls")]
[BsonIgnoreExtraElements]
public class Call
{
[BsonId]
public CallId _id { get; set; }
[BsonElement("responses")]
[BsonIgnoreIfNull]
public IList<DataRecord> Responses { get; set; }
}
Also asked here: https://groups.google.com/forum/#!topic/mongodb-user/iOeEXbUYbo4 此处还询问: https : //groups.google.com/forum/#!topic / mongodb-user / iOeEXbUYbo4
I think your better bet in this situation is to use a custom discriminator convention. 我认为在这种情况下你最好的选择是使用自定义鉴别器约定。 You can see an example of this here: https://github.com/mongodb/mongo-csharp-driver/blob/v1.x/MongoDB.DriverUnitTests/Samples/MagicDiscriminatorTests.cs . 你可以在这里看到一个例子: https : //github.com/mongodb/mongo-csharp-driver/blob/v1.x/MongoDB.DriverUnitTests/Samples/MagicDiscriminatorTests.cs 。 While this example is based on whether a field exists in the document, you could easily base it on what type the field is (BsonType.Int32, BsonType.Date, etc...). 虽然此示例基于文档中是否存在字段,但您可以轻松地将其基于字段的类型(BsonType.Int32,BsonType.Date等...)。
Basing on @Craig Wilson answer, to get rid off all discriminators, you can: 基于@Craig Wilson回答,为了摆脱所有歧视者,你可以:
public class NoDiscriminatorConvention : IDiscriminatorConvention
{
public string ElementName => null;
public Type GetActualType(IBsonReader bsonReader, Type nominalType) => nominalType;
public BsonValue GetDiscriminator(Type nominalType, Type actualType) => null;
}
and register it: 并注册:
BsonSerializer.RegisterDiscriminatorConvention(typeof(BaseEntity), new NoDiscriminatorConvention());
This problem occurred in my case when I was adding a Dictionary<string, object> and List entities to database.当我将 Dictionary<string, object> 和 List 实体添加到数据库时,就出现了这个问题。 The following link helped me in this regard: C# MongoDB complex class serialization .以下链接在这方面帮助了我: C# MongoDB complex class serialization 。 For your case, I would suggest, following the link given above, as follows:对于您的情况,我建议按照上面给出的链接,如下所示:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
[JsonConverter(typeof(DataRecordConverter))]
public abstract class DataRecord
{
public string Key { get; set; }
public string DataRecordType {get;}
}
public class DataRecordInt : DataRecord
{
public int Value { get; set; }
public override string DataRecordType => "Int"
}
public class DataRecordDateTime : DataRecord
{
public DateTime? Value { get; set; }
public override string DataRecordType => "DateTime"
}
public class DataRecordConverter: JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DataRecord);
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var dataRecord = default(DataRecord);
switch (jsonObject["DataRecordType"].Value<string>())
{
case "Int":
dataRecord = new DataRecordInt();
break;
case "DateTime":
dataRecord = new DataRecordDataTime();
break;
}
serializer.Populate(jsonObject.CreateReader, dataRecord);
return dataRecord;
}
}
[BsonCollectionName("calls")]
[BsonIgnoreExtraElements]
public class Call
{
[BsonId]
public CallId _id { get; set; }
[JsonIgnore]
[BsonElement("responses")]
[BsonIgnoreIfNull]
public BsonArray Responses { get; set; }
}
You can add populate the BsonArray using:您可以使用以下方法添加填充 BsonArray:
var jsonDoc = JsonConvert.SerializeObject(source);
var bsonArray = BsonSerializer.Deserialize<BsonArray>(jsonDoc);
Now, you can get deserialized List from Mongo using:现在,您可以使用以下方法从 Mongo 获取反序列化列表:
var bsonDoc = BsonExtensionMethods.ToJson(source);
var dataRecordList = JsonConvert.DeserializeObject<List<DataRecord>>(bsonDoc, new DataRecordConverter())
Hope this helps, again, thanks to C# MongoDB complex class serialization for this.希望这有助于再次感谢C# MongoDB 复杂类序列化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.