web-dev-qa-db-fra.com

Erreur de conversion JSON.NET lors de la sérialisation de l'ObjectId Mongo

Je suis en train de jouer avec MongoDB et j'ai un objet avec un ObjectId mongodb dessus . Quand je sérialise cela avec la méthode .NET Json (), tout va bien (mais les dates sont horribles!)

Si j'essaie cela avec le sérialiseur JSON.NET, cela me donne une exception InvalidCastException lorsque je tente de sérialiser l'ObjectID

des idées ce qui se passe et comment je peux résoudre ce problème?

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.

Si vous modifiez la méthode du contrôleur pour utiliser le sérialiseur livré avec .NET, cela fonctionne correctement (mais, celui-ci donne des dates laides, mais bon)

public JsonResult NiceJsonPlease()
    {

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

        return Json(q, JsonRequestBehavior.AllowGet);
    }
26
Keeno

J'avais un pointeur du groupe d'utilisateurs MongoDB ..__ https://groups.google.com/forum/?fromgroups=#!topic/mongodb-csharp/A_DXHuPscnQ

La réponse était .__ "Cela semble être un problème de Json.NET, mais pas vraiment. Il existe ici un type personnalisé qu'il ignore tout simplement. Vous devez dire à Json.NET comment sérialiser un ObjectId."

J'ai donc implémenté la solution suivante

J'ai décoré mon ObjectId avec 

[JsonConverter(typeof(ObjectIdConverter))]

Puis écrit un convertisseur personnalisé qui crache simplement la partie Guid de 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;
    }


}
23
Keeno

Vous pouvez utiliser le type de chaîne .NET au lieu d’ObjectId. Si vous utilisez BsonDateTime, vous aurez le même problème de conversion. Il s'agit d'une classe de domaine dans mon projet qui utilise ces décorateurs.

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; }
}
46
Andy

1) Ecrire un convertisseur 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) Enregistrez-le dans JSON.NET globalement avec des paramètres globaux et vous n’avez pas besoin de marquer vos modèles avec de grands attributs.

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

J'ai résolu un problème similaire que je rencontrais avec l'erreur du sérialiseur JSON.NET/InvalidCastException en définissant le paramètre JsonOutputMode sur strict, ce qui évitait de devoir modifier le type sous-jacent:

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

Informations supplémentaires disponibles dans l'API: http://api.mongodb.org/csharp/1.8.3/html/d73bf108-d68c-e472-81af-36ac29ea08da.htm

7
lintunen

J'ai rencontré un problème similaire avec un projet d'API Web et j'ai fini par me frapper la tête contre le clavier pendant quelques heures avant de trouver ce fil.

Au début, tout fonctionnait bien, mais le problème est survenu après la conversion de mon code pour utiliser ma propre classe personnalisée au lieu de l'objet BsonDocument, comme recommandé dans la documentation du pilote mongoDB C #.

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

Voici l'équivalent VB.net de la solution ci-dessus pour ceux qui en ont besoin;

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
2
Drifter

J'ai utilisé ce code dans VB.Net et ai travaillé parfaitement, vous pouvez voir le objectId dans la classe et vous pouvez faire la même chose avec le type de données 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
0
Diego