[英]Mongo C# Driver and ObjectID JSON String Format in .NET Core
I have a collection of dynamic data.我有一组动态数据。 I want to get it back like this:我想像这样取回它:
{
_id: "58b454f20960a1788ef48ebb"
...
}
Here are a list of approaches that do not work:以下是不起作用的方法列表:
This这
await resources = _database.GetCollection<BsonDocument>("resources")
.Find(Builders<BsonDocument>.Filter.Empty)
.ToListAsync();
return Ok(resources);
Yields产量
[[{"name":"_id","value":{"bsonType":7,"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z","rawValue":{"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z"},"value":{"timestamp":1488213234,"machine":614561,"pid":30862,"increment":16027323,"creationTime":"2017-02-27T16:33:54Z"}}}]]
This这
await resources = _database.GetCollection<BsonDocument>("resources")
.Find(Builders<BsonDocument>.Filter.Empty)
.ToListAsync();
return Ok(resources.ToJson());
Yields产量
[{ "_id" : ObjectId("58b454f20960a1788ef48ebb"), ... }]
This这
await resources = _database.GetCollection<BsonDocument>("resources")
.Find(Builders<BsonDocument>.Filter.Empty)
.ToListAsync();
return Ok(resources.ToJson(new JsonWriterSettings() { OutputMode = JsonOutputMode.Strict }));
Yields产量
[{ "_id" : { "$oid" : "58b454f20960a1788ef48ebb" }, ... }]
This这
await resources = _database.GetCollection<BsonDocument>("resources")
.Find(Builders<BsonDocument>.Filter.Empty)
.ToListAsync();
return Ok(Newtonsoft.Json.JsonConvert.SerializeObject(resources));
Yields产量
"Newtonsoft.Json.JsonSerializationException: Error getting value from 'AsBoolean' on 'MongoDB.Bson.BsonObjectId'. ---> System.InvalidCastException: Unable to cast object of type 'MongoDB.Bson.BsonObjectId' to type 'MongoDB.Bson.BsonBoolean' “Newtonsoft.Json.JsonSerializationException:从“MongoDB.Bson.BsonObjectId”上的“AsBoolean”获取值时出错。---> System.InvalidCastException:无法将“MongoDB.Bson.BsonObjectId”类型的对象转换为“MongoDB.Bson” .BsonBoolean'
And changing BsonDocument
to dynamic
yields the same results.将BsonDocument
更改为dynamic
产生相同的结果。
I have also tried registering a serializer according to the docs .我还尝试根据文档注册序列化程序。 I really like this solution since I always want my ObjectId
s in a reasonable format and not in something unusable.我真的很喜欢这个解决方案,因为我总是希望我的ObjectId
具有合理的格式,而不是无法使用的格式。 I would like to get this working if possible.如果可能的话,我想让这个工作。
This这
_client = new MongoClient(clientSettings);
_database = _client.GetDatabase(_settings.DatabaseName);
BsonSerializer.RegisterSerializer(new ObjectIdSerializer());
...
class ObjectIdSerializer : SerializerBase<ObjectId>
{
public override ObjectId Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return context.Reader.ReadObjectId();
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, ObjectId value)
{
context.Writer.WriteString(value.ToString());
}
}
Had no effect on any of the above results.对上述任何结果都没有影响。
After trying a number of different configurations, the only way I was able to correctly save truly dynamic documents using the connector was to parse objects as BsonDocument
s.在尝试了许多不同的配置之后,我能够使用连接器正确保存真正动态文档的唯一方法是将对象解析为BsonDocument
。
public ActionResult Post([FromBody]JObject resource)
{
var document = BsonDocument.Parse(resource.ToString(Formatting.None));
DbContext.Resources.InsertOne(document);
}
BsonDocument
serializers with JSON.Net使用 JSON.Net 注册BsonDocument
序列化程序The problem with the above approach initially was that when calling ToJson()
the ISODate
and ObjectId
objects would be serialized into objects, which was undesirable.上述方法最初的问题是,在调用ToJson()
, ISODate
和ObjectId
对象将被序列化为对象,这是不可取的。 At the time of writing, there doesn't seem to be any extensibility points for overriding this behavior.在撰写本文时,似乎没有任何可覆盖此行为的扩展点。 The logic is baked into the MongoDB.Bson.IO.JsonWriter
class , and you cannot register BsonSerializer
s for BsonValue
types:该逻辑被烘焙到MongoDB.Bson.IO.JsonWriter
类中,并且您不能为BsonValue
类型注册BsonSerializer
:
MongoDB.Bson.BsonSerializationException: A serializer cannot be registered for type BsonObjectId because it is a subclass of BsonValue. MongoDB.Bson.BsonSerializationException:无法为类型 BsonObjectId 注册序列化程序,因为它是 BsonValue 的子类。
At the time of writing, the only solution I've found is to explicitly custom JSON.Net converters.在撰写本文时,我发现的唯一解决方案是显式自定义 JSON.Net 转换器。 MongoDB C# Lead Robert Stam has created an unpublished library for this which community member Nathan Robinson has ported to .net-core. MongoDB C# 负责人 Robert Stam为此创建了一个未发布的库,社区成员 Nathan Robinson 已将其移植到 .net-core。 . . I've created a fork that properly serializes the ObjectId and ISODate fields.我创建了一个正确序列化 ObjectId 和 ISODate 字段的分支。
I've created a NuGet package from their work.我从他们的工作中创建了一个 NuGet 包。 To use it, include the following reference in your .csproj
file:要使用它,请在.csproj
文件中包含以下引用:
<PackageReference Include="MongoDB.Integrations.JsonDotNet" Version="1.0.0" />
Then, explicitly register the converters:然后,显式注册转换器:
Startup.cs启动文件
using MongoDB.Integrations.JsonDotNet.Converters;
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
// Adds automatic json parsing to BsonDocuments.
options.SerializerSettings.Converters.Add(new BsonArrayConverter());
options.SerializerSettings.Converters.Add(new BsonMinKeyConverter());
options.SerializerSettings.Converters.Add(new BsonBinaryDataConverter());
options.SerializerSettings.Converters.Add(new BsonNullConverter());
options.SerializerSettings.Converters.Add(new BsonBooleanConverter());
options.SerializerSettings.Converters.Add(new BsonObjectIdConverter());
options.SerializerSettings.Converters.Add(new BsonDateTimeConverter());
options.SerializerSettings.Converters.Add(new BsonRegularExpressionConverter());
options.SerializerSettings.Converters.Add(new BsonDocumentConverter());
options.SerializerSettings.Converters.Add(new BsonStringConverter());
options.SerializerSettings.Converters.Add(new BsonDoubleConverter());
options.SerializerSettings.Converters.Add(new BsonSymbolConverter());
options.SerializerSettings.Converters.Add(new BsonInt32Converter());
options.SerializerSettings.Converters.Add(new BsonTimestampConverter());
options.SerializerSettings.Converters.Add(new BsonInt64Converter());
options.SerializerSettings.Converters.Add(new BsonUndefinedConverter());
options.SerializerSettings.Converters.Add(new BsonJavaScriptConverter());
options.SerializerSettings.Converters.Add(new BsonValueConverter());
options.SerializerSettings.Converters.Add(new BsonJavaScriptWithScopeConverter());
options.SerializerSettings.Converters.Add(new BsonMaxKeyConverter());
options.SerializerSettings.Converters.Add(new ObjectIdConverter());
});
}
}
Now, you can serialize using the default serializer:现在,您可以使用默认序列化程序进行序列化:
return Created($"resource/{document["_id"].ToString()}", document);
You can make your last attempt work by registering custom ObjectIdConverter
with NewtonSoft.您可以通过向 NewtonSoft 注册自定义ObjectIdConverter
来完成最后一次尝试。
await resources = _database.GetCollection<dynamic>("resources")
.Find(Builders<dynamic>.Filter.Empty)
.ToListAsync();
return Ok(Newtonsoft.Json.JsonConvert.SerializeObject(resources, new ObjectIdConverter()));
Converter:转换器:
class ObjectIdConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(ObjectId).IsAssignableFrom(objectType);
}
}
Note: The above converter converts from ObjectId
to String after the BSONSerailzers
have converted bson value to ObjectId
.注意:从上述转换器转换ObjectId
到字符串后BSONSerailzers
具有转换后的BSON值ObjectId
。
You'll still need to use parse to convert string id to ObjectIds for queries and register the ObjectIdConverter globally.您仍然需要使用 parse 将字符串 id 转换为 ObjectIds 以进行查询并全局注册 ObjectIdConverter。
Reference: https://stackoverflow.com/a/16693462/2683814参考: https : //stackoverflow.com/a/16693462/2683814
A "terrible" way of solving this is to convert your BsonDocument to a Dictionary in case your object is a plain object.解决此问题的“可怕”方法是将您的 BsonDocument 转换为字典,以防您的对象是普通对象。
[HttpGet]
public async Task<IActionResult> Get()
{
var items = (await collection.Find(new BsonDocument()).ToListAsync());
var obj = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(items.ToJson());
return Ok(obj);
}
This method is simple to code but I see a lot of overhead for conversions.这种方法易于编码,但我看到很多转换开销。
The best way is to change the Asp.Net serializer to return the "items.ToJson()" as the response Content without trying to parse it.最好的方法是更改 Asp.Net 序列化程序以返回“items.ToJson()”作为响应内容而不尝试解析它。
The old (but gold) HttpRequestMessage enables it.旧的(但黄金) HttpRequestMessage 启用它。 (I can't have time to create a sample to share here now) (我现在没有时间创建一个样本在这里分享)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.