Словарь -к-BsonDocument преобразования опуская поля Т
Я использую ToBsonDocument
метод расширения от MongoDB.Bson
преобразовать этот словарь:
var dictionary = new Dictionary<string, object> {{"person", new Dictionary<string, object> {{"name", "John"}}}};
var document = dictionary.ToBsonDocument();
и вот итоговый документ:
{ "person" :
{ "_t" : "System.Collections.Generic.Dictionary`2[System.String,System.Object]",
"_v" : { "name" : "John" } } }
есть ли способ избавиться от этих ІТ/_в вещи? Я хотел бы, чтобы полученный документ выглядел так:
{ "person" : { "name" : "John" } }
UPD: я нашел код в DictionaryGenericSerializer:
if (nominalType == typeof(object))
{
var actualType = value.GetType();
bsonWriter.WriteStartDocument();
bsonWriter.WriteString("_t", TypeNameDiscriminator.GetDiscriminator(actualType));
bsonWriter.WriteName("_v");
Serialize(bsonWriter, actualType, value, options); // recursive call replacing nominalType with actualType
bsonWriter.WriteEndDocument();
return;
}
Итак, кажется, что с этим сериализатором не так много вариантов, когда тип значения object
.
4 ответов
сначала вы должны сериализоваться в JSON, а затем в BSON,
var jsonDoc = Newtonsoft.Json.JsonConvert.SerializeObject(dictionary);
var bsonDoc = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(jsonDoc);
это происходит потому, что вы указываете object
введите значения словаря, но на самом деле используйте Dictionary<string, object>
введите конкретное значение записи. Поэтому драйвер CSharp сохраняет полное имя конкретного типа для десериализации этого документа в будущем. Вы также можете прочитать больше об этом здесь: сериализация документов с помощью драйвера CSharp: полиморфные классы и дискриминаторы
для достижения желаемого результата необходимо указать конкретный тип словаря значения:
var dictionary = new Dictionary<string, Dictionary<string, object>>
{
{ "person", new Dictionary<string, object> { { "name", "John" } } }
};
var document = dictionary.ToBsonDocument();
прямое преобразование из объекта в BSON затруднено.
вместо этого перейдите от object --> в формате JSON --> Bson
// Object --> JSON
using System.Web.Extensions;
using System.Web;
using System.Web.Script.Serialization;
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(poco); // poco is ur class object
//JSON --> BSON
MongoDB.Bson.BsonDocument document = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json);
переход от объектов .NET к JSON к BSON, в то же время избегая кодирования _t/_v, приведет к потере информации о типе для дат и других типов, специфичных для BSON.
решение, которое я нашел для избежания _t / _v при сохранении типов BSON, состояло в регистрации пользовательского сериализатора значений для встроенного драйвера DictionaryInterfaceImplementerSerializer
(и при необходимости EnumerableInterfaceImplementerSerializer
тоже). Я протестировал решение для генерации "Extended JSON", т. е. JSON со специальным синтаксисом для BSON-конкретные типы, но принцип должен быть одинаковым для прямого вывода BSON.
для этого сначала скопируйте стандартный ObjectSerializer
в ваш проект C# (и переименуйте его, чтобы избежать двусмысленности): https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Bson/Serialization/Serializers/ObjectSerializer.cs
в дополнение к переименованию класс private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType)
метод также необходимо отредактировать. (В моем случае, я также хотел избежать ІТ/_в кодировку IEnumerable<object>
, что отражено ниже.)
private void SerializeDiscriminatedValue(BsonSerializationContext context, BsonSerializationArgs args, object value, Type actualType)
{
var serializer = BsonSerializer.LookupSerializer(actualType);
var polymorphicSerializer = serializer as IBsonPolymorphicSerializer;
// Added line
var assignableToDictionaryOrEnumerable = typeof(IDictionary<string, object>).IsAssignableFrom(actualType) || typeof(IEnumerable<object>).IsAssignableFrom(actualType);
if (polymorphicSerializer != null && polymorphicSerializer.IsDiscriminatorCompatibleWithObjectSerializer)
{
serializer.Serialize(context, args, value);
}
else
{
// Edited line
if (assignableToDictionaryOrEnumerable || (context.IsDynamicType != null && context.IsDynamicType(value.GetType())))
{
// We want this code to be executed for types that should be serialized without _t and _v fields
args.NominalType = actualType;
serializer.Serialize(context, args, value);
}
else
{
var bsonWriter = context.Writer;
var discriminator = _discriminatorConvention.GetDiscriminator(typeof(object), actualType);
bsonWriter.WriteStartDocument();
bsonWriter.WriteName(_discriminatorConvention.ElementName);
BsonValueSerializer.Instance.Serialize(context, discriminator);
bsonWriter.WriteName("_v");
serializer.Serialize(context, value);
bsonWriter.WriteEndDocument();
}
}
}
предполагая, что имя скопированного и отредактированного класса DynamicValueSerializer
использовать следующий код, чтобы зарегистрировать его в качестве сериализатора значение при запуске приложения.
BsonSerializer.RegisterSerializer(typeof(Dictionary<string, object>),
new DictionaryInterfaceImplementerSerializer<Dictionary<string, object>, string, object>(
DictionaryRepresentation.Document,
new StringSerializer(),
new DynamicValueSerializer()));
обратите внимание, что это должно быть сделано для каждого фактического типа, для значений которого он должен использоваться. Регистрация сериализатора не может быть выполнена для интерфейсов. Таким образом, если SortedDictionary<string, object>
или SortedList<string, object>
должны рассматриваться так же, как Dictionary<string, object>
Регистрация нужно повторить для этих типов. (Это влияет ли словари содержится как значения в коллекциях этих типов будут сериализованы без _t / _v, не обязательно, будут ли объекты этих типов сериализованы таким образом.)
применить тот же принцип сериализации List<object>
объекты, используйте следующий регистрационный код.
BsonSerializer.RegisterSerializer(typeof(List<object>),
new EnumerableInterfaceImplementerSerializer<List<object>, object>(
new DynamicValueSerializer()));