【问题标题】:JSON.NET cast error when serializing Mongo ObjectId序列化 Mongo ObjectId 时出现 JSON.NET 转换错误
【发布时间】:2013-05-15 03:27:45
【问题描述】:

我正在使用 MongoDB,并且有一个带有 mongodb ObjectId 的对象。 当我使用 .NET Json() 方法对其进行序列化时,一切都很好(但日期很糟糕!)

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

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

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.

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

public JsonResult NiceJsonPlease()
    {

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

        return Json(q, JsonRequestBehavior.AllowGet);
    }

【问题讨论】:

  • 啊,旧 Json 日期问题:hanselman.com/blog/…
  • @Liam 嗨,Liam,我实际上是在今天早些时候阅读了该博客,这也是我提出这个问题的原因。 JSON.NET 为我提供了更好的日期,但如果我在模型中包含 MongoDB ObjectID 并尝试使用 JSON.NET 对其进行序列化,则会出错。
  • 你能展示你的代码吗?
  • 在属性上使用属性 [BsonDefaultValue(0)] 并使用 ProjectionDefinition 从 MongoDB [BsonDefaultValue(0)] public long SomePropery;解决方案是删除属性。

标签: c# mongodb json.net


【解决方案1】:

你可以使用.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; }
}

【讨论】:

  • 这很有效。感谢您提供日期。
【解决方案2】:

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

反应是

这似乎是一个 Json.NET 问题,但实际上并非如此。有一种习俗 在这里输入它根本不知道。你需要告诉 Json.NET 如何 序列化一个 ObjectId。

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

我用

装饰了我的 ObjectId
[JsonConverter(typeof(ObjectIdConverter))]

然后编写了一个自定义转换器,它只吐出 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;
    }


}

【讨论】:

  • 这对我来说非常有用。我在这里找到了一个同名类的完整版本:github.com/geersch/MySinglePageApplication/blob/master/…
  • @Syed Muhammed Mubashir,要完成这项工作,只需在 ObjectId 字段上方添加一个转换属性。 [JsonConverter(typeof(ObjectIdConverter))] public ObjectId SiteUserId { get;放; }
  • 更好的方法 - 全局注册转换器 - _serializerSettings = new JsonSerializerSettings() { Converters = new List { new ObjectIdConverter() } };
  • 这为我指明了正确的方向,我在将 BsonDocument 转换为 JSON 时遇到了问题。非常感谢。
  • @DougJohnson - 我尝试了上述解决方案,但出现错误。我在stackoverflow.com/questions/69446519/… 中发布了问题
【解决方案3】:

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) 使用全局设置在 JSON.NET 中全局注册它,你不需要用大属性标记你的模型

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

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

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

【讨论】:

  • 能否请您列出您使用的进口产品。我假设:使用系统;使用 MongoDB.Bson;使用 Newtonsoft.Json;
【解决方案4】:

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

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

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

【讨论】:

  • 谢谢!我知道它必须是这样的。我在转换 JsonString 时遇到问题,因为它包含无效类型。
  • 完美答案,你应该为此获得一枚奖牌。我找了 2 天。
  • 你应该加薪
【解决方案5】:

我在一个 Web API 项目中遇到了类似的问题,并且在我找到这个帖子之前,我在键盘上敲了几个小时。

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

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

对于那些需要它的人来说,这里是与上述解决方案等效的 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

【讨论】:

    【解决方案6】:

    还有一个选择

    object dotnetObject = BsonTypeMapper.MapToDotNetValue(bsonDocument);
    
    // Json mapped to default .Net objects
    string json = Newtonsoft.Json.JsonConvert.SerializeObject(dotnetObject);
    
    // Parsing as JObject
    var jobject = JObject.Parse(json);
    
    // Deserializing as your custom Type
    var myObject = JsonConvert.DeserializeObject<MyType>(json);
    

    【讨论】:

    • 它适用于在 Xamarin.iOS 中转换 MongoDBRealm 对象。必须先将 RealmObject 转换为 BsonDocument。
    【解决方案7】:

    我在 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多