Ошибка при запуске JSON.NET при сериализации Mongo ObjectId
Я играю с MongoDB и имею объект с объектом mindodb ObjectId.
Когда я сериализую это с помощью метода .NET Json(), все хорошо (но даты ужасны!)
Если я попробую это с помощью сериализатора JSON.NET, это даст мне InvalidCastException при попытке сериализации ObjectID
любые идеи, что происходит, и как я могу это исправить?
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, он работает нормально (но это дает уродливые даты, blugh)
public JsonResult NiceJsonPlease()
{
var q = new TestClass();
q.id = new ObjectId();
q.test = "just updating this";
return Json(q, JsonRequestBehavior.AllowGet);
}
Ответы
Ответ 1
У меня был указатель из группы пользователей MongoDB.
https://groups.google.com/forum/?fromgroups=#!topic/mongodb-csharp/A_DXHuPscnQ
Ответ был
"Кажется, это проблема Json.NET, но на самом деле не существует. Пользовательский тип здесь просто не знает. Вам нужно рассказать Json.NET о том, как сериализовать ObjectId."
Итак, я выполнил следующее решение
Я украсил свой объект ObjectId
[JsonConverter(typeof(ObjectIdConverter))]
Затем написал пользовательский конвертер, который просто выплевывает часть Guid объекта ObjectId
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;
}
}
Ответ 2
Вы можете использовать строковый тип .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; }
}
Ответ 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; }
Ответ 4
Я решил аналогичную проблему, с которой я столкнулся с ошибкой serializer/InvalidCastException JSON.NET, установив строгую процедуру JsonOutputMode, которая устранила необходимость изменения базового типа:
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
Ответ 5
У меня возникла аналогичная проблема с проектом веб-API, и я потерял голову над клавиатурой в течение нескольких часов, прежде чем нашел этот поток.
Изначально все работало нормально, но затем я столкнулся с проблемой после преобразования моего кода, чтобы использовать свой собственный пользовательский класс вместо объекта BsonDocument, как рекомендовано в документации драйвера mongoDB С#.
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
Я использовал этот код в 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