C# 序列化 Mongo ObjectId 时出现 JSON.NET 转换错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16651776/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:34:04  来源:igfitidea点击:

JSON.NET cast error when serializing Mongo ObjectId

c#mongodbjson.net

提问by Keeno

I am playing around with MongoDB and have an object with a mongodb ObjectId on it. When I serialise this with the .NET Json() method, all is good (but the dates are horrible!)

我正在玩 MongoDB 并且有一个带有 mongodb ObjectId 的对象。当我用 .NET Json() 方法序列化它时,一切都很好(但日期太可怕了!)

If I try this with the JSON.NET serialiser it gives me an InvalidCastException when trying to serialise the ObjectID

如果我使用 JSON.NET 序列化程序尝试此操作,它会在尝试序列化 ObjectID 时给我一个 InvalidCastException

any ideas whats happening and how I can fix this?

有什么想法发生了什么以及我如何解决这个问题?

using MongoDB.Driver;
using MongoDB.Bson;
using Newtonsoft.Json;

//this is a route on a controller
   public string NiceJsonPlease()
    {

        var q = new TestClass();
        q.id = new ObjectId();
        q.test = "just updating this";

        return JsonConvert.SerializeObject(q);
    }

    //simple test class
    class TestClass
    {
        public ObjectId id; //MongoDB ObjectID
        public string test = "hi there";
    }


Exception Details: System.InvalidCastException: Specified cast is not valid.

If you change the controller method to use the serializer that ships with .NET, it works ok (but, this one gives ugly dates, blugh)

如果您将控制器方法更改为使用 .NET 附带的序列化程序,它可以正常工作(但是,这个日期很丑,blugh)

public JsonResult NiceJsonPlease()
    {

        var q = new TestClass();
        q.id = new ObjectId();
        q.test = "just updating this";

        return Json(q, JsonRequestBehavior.AllowGet);
    }

采纳答案by Keeno

I had a pointer from the MongoDB user group. https://groups.google.com/forum/?fromgroups=#!topic/mongodb-csharp/A_DXHuPscnQ

我有一个来自 MongoDB 用户组的指针。 https://groups.google.com/forum/?fromgroups=#!topic/mongodb-csharp/A_DXHuPscnQ

The response was "This seems to be a Json.NET issue, but not really. There is a custom type here it simply doesn't know about. You need to tell Json.NET how to serialize an ObjectId."

响应是“这似乎是 Json.NET 问题,但实际上并非如此。这里有一个自定义类型,它根本不知道。您需要告诉 Json.NET 如何序列化 ObjectId。”

So, I implemented the following solution

所以,我实施了以下解决方案

I decorated my ObjectId with

我装饰了我的 ObjectId

[JsonConverter(typeof(ObjectIdConverter))]

Then wrote a custom converter that just spits out the Guid portion of the ObjectId

然后写了一个自定义转换器,它只是吐出 ObjectId 的 Guid 部分

 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);
        //return true;
    }


}

回答by Andy

You can use .NET string type instead of ObjectId, You just need to decorate it with BsonRepresentation. If you use BsonDateTime, you will have the same conversion issue. This is a domain class in my project that uses those decorators.

您可以使用 .NET 字符串类型代替 ObjectId,您只需要使用 BsonRepresentation 对其进行修饰即可。如果您使用 BsonDateTime,您将遇到相同的转换问题。这是我的项目中使用这些装饰器的域类。

public class DocumentMetadata
{
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public string Name { get; set; }
    public string FullName { get; set; }

    [BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
    public DateTime DownloadTime { get; set; }
}

回答by lintunen

I resolved a similar problem I was experiencing with the JSON.NET serializer/InvalidCastException error by setting the JsonOutputMode to strict, which eradicated the need to change the underlying type:

我通过将 JsonOutputMode 设置为严格解决了我在 JSON.NET 序列化程序/InvalidCastException 错误中遇到的类似问题,这消除了更改基础类型的需要:

var jsonWriterSettings = new JsonWriterSettings { OutputMode = JsonOutputMode.Strict };
var json = doc.ToJson(jsonWriterSettings);

With further information available in the API: http://api.mongodb.org/csharp/1.8.3/html/d73bf108-d68c-e472-81af-36ac29ea08da.htm

API 中提供了更多信息:http: //api.mongodb.org/csharp/1.8.3/html/d73bf108-d68c-e472-81af-36ac29ea08da.htm

回答by Drifter

I ran into a similar problem with a Web API project, and wound up beating my head against the keyboard for a few hours before I found this thread.

我在一个 Web API 项目中遇到了类似的问题,在我找到这个线程之前,我的头靠在键盘上几个小时。

Initially everything was working fine, but then I ran into the problem after converting my code to use my own custom class instead of the BsonDocument object as recommended in the mongoDB C# driver documentation.

最初一切正常,但是在将我的代码转换为使用我自己的自定义类而不是 mongoDB C# 驱动程序文档中推荐的 BsonDocument 对象后,我遇到了问题。

http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-csharp-driver/#bsondocument-object-model-vs-your-own-domain-classes

http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-csharp-driver/#bsondocument-object-model-vs-your-own-domain-classes

Here the VB.net equivalent to the solution above for those that need it;

这里的 VB.net 相当于上面的解决方案,供需要的人使用;

Public Class DocumentMetadata
    <BsonId> _
    <BsonRepresentation(BsonType.ObjectId)> _
    Public Property Id() As String
    Public Property Name() As String
    Public Property FullName() As String

    <BsonDateTimeOptions(Kind := DateTimeKind.Utc)> _
    Public Property DownloadTime() As DateTime
End Class

回答by ZOXEXIVO

1) Write ObjectId converter

1) 编写 ObjectId 转换器

public class ObjectIdConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ObjectId);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType != JsonToken.String)
            throw new Exception($"Unexpected token parsing ObjectId. Expected String, got {reader.TokenType}.");

        var value = (string)reader.Value;
        return string.IsNullOrEmpty(value) ? ObjectId.Empty : new ObjectId(value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is ObjectId)
        {
            var objectId = (ObjectId)value;
            writer.WriteValue(objectId != ObjectId.Empty ? objectId.ToString() : string.Empty);
        }
        else
        {
            throw new Exception("Expected ObjectId value.");
        }
    }
}

2) Register it in JSON.NET globally with global settings and you not need mark you models with big attributes

2) 使用全局设置在 JSON.NET 中全局注册它,你不需要用大属性标记你的模型

            var _serializerSettings = new JsonSerializerSettings()
            {
                Converters = new List<JsonConverter> { new ObjectIdConverter() }
            };

3) Big advice - don't use ObjectId in your models - use string

3) 重要建议 - 不要在模型中使用 ObjectId - 使用字符串

[BsonRepresentation(BsonType.ObjectId]
public string Id{ get;set; }

回答by Diego

I used this code in VB.Net and worked perfect, you can see the objectId in the class and you can do the same thing with the data type DATE.

我在 VB.Net 中使用了这段代码并且工作得很好,你可以在类中看到 objectId,你可以用数据类型 DATE 做同样的事情。

    Imports MongoDB.Bson
    Imports MongoDB.Bson.Serialization.Attributes    
    Imports MongoDB.Driver

Public Class _default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim objMongo As New MongoClient("mongodb://192.168.111.5:27017")
        Dim objDatabase As IMongoDatabase = objMongo.GetDatabase("local")
        Dim objCollection = objDatabase.GetCollection(Of BsonDocument)("Test")            
        Dim _ret As New List(Of mongo_users)

        Dim result = objCollection.Find(New BsonDocument()).ToList()
        Dim _json_response = result.ToJson()
        If _json_response <> "" Then

            _ret = MongoDB.Bson.Serialization.BsonSerializer.Deserialize(Of List(Of mongo_users))(_json_response)

        End If

        For Each item In _ret
            Response.Write(item.name & " " & item.last_name & "</br>")
        Next


    End Sub

End Class

Public Class mongo_users            
    <BsonId>
    <BsonRepresentation(BsonType.ObjectId)>
    Public Property _id() As String
    Public Property status As Integer
    Public Property name As String
    Public Property last_name As String
    Public Property colors As List(Of user_colors)    
End Class

Public Class user_colors
    Public Property color_name As String
End Class