2016-03-23 5 views
1

Ich habe versucht, dieses Problem für mehrere Stunden zu beheben. Ich habe viele Ansätze ausprobiert und viele Beiträge gelesen. Ich kann keine Lösung finden.Json.Net Deserialisierung von komplexen Objekten - Basisklasse, die eine Basisklasse als Eigenschaft enthält

Ich habe eine Objekthierarchie, wobei ich Typen abgeleitet habe, die einen anderen abgeleiteten Typ enthalten. Ich benutze Json.Net's TypeNameHandling Einstellung. Ich habe es auf Auto auf Serialisierung und Deserialisierung festgelegt.

Das resultierende JSON ist:

"[\r\n {\r\n \"$type\": \"Common.Monitors.HeartbeatMonitor, Common\",\r\n \"ClassName\": \"HeartbeatMonitor\",\r\n \"ServerGroupMonitorId\": 1,\r\n \"Instructions\": {\r\n  \"$type\": \"Common.Monitors.HeartbeatMonitorInstructions, Common\",\r\n  \"RunIntervalInMinutes\": 1\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:35:18.7458519\"\r\n },\r\n {\r\n \"$type\": \"Common.Monitors.DiskSpaceMonitor, Common\",\r\n \"ClassName\": \"DiskSpaceMonitor\",\r\n \"ServerGroupMonitorId\": 2,\r\n \"Instructions\": {\r\n  \"$type\": \"Common.Monitors.DiskSpaceMonitorInstructions, Common\",\r\n  \"DriveLetter\": \"C:\\\",\r\n  \"AlertWhenPercentFreeLessThan\": 20,\r\n  \"RunIntervalInMinutes\": 30\r\n },\r\n \"LastExecutionDateTime\": \"2016-03-22T16:15:18.7458519\"\r\n }\r\n]" 

Bei Verwendung der folgenden deserialisieren Versuch:

string json = response.Content.ReadAsStringAsync().Result; 

IEnumerable<MonitorBase> monitors = JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(json, new JsonSerializerSettings 
      { 
       TypeNameHandling = TypeNameHandling.Auto 
      }); 

I die folgende Ausnahme:

eine Ausnahme vom Typ ‚Newtonsoft. Json.JsonSerializationException 'ist in Newtonsoft.Json.dll aufgetreten, wurde aber nicht im Benutzercode behandelt.

Zusätzliche Informationen: Fehler beim Konvertieren Wert

"[ 
    { 
    "$type": "Common.Monitors.HeartbeatMonitor, Common", 
    "ClassName": "HeartbeatMonitor", 
    "ServerGroupMonitorId": 1, 
    "Instructions": { 
     "$type": "Common.Monitors.HeartbeatMonitorInstructions, Common", 
     "RunIntervalInMinutes": 1 
    }, 
    "LastExecutionDateTime": "2016-03-22T16:35:18.7458519" 
    }, 
    { 
    "$type": "Common.Monitors.DiskSpaceMonitor, Common", 
    "ClassName": "DiskSpaceMonitor", 
    "ServerGroupMonitorId": 2, 
    "Instructions": { 
     "$type": "Common.Monitors.DiskSpaceMonitorInstructions, Common", 
     "DriveLetter": "C:\\", 
     "AlertWhenPercentFreeLessThan": 20, 
     "RunIntervalInMinutes": 30 
    }, 
    "LastExecutionDateTime": "2016-03-22T16:15:18.7458519" 
    } 
]" 

'System.Collections.Generic.IEnumerable`1 [Common.Monitors.MonitorBase]' eingeben. Pfad '', Zeile 1, Position 943.

Die HeartbeatMonitor und DiskSpaceMonitor Typen ableiten von MonitorBase und ihre jeweiligen Instructions Typen stammen aus MonitorInstructionBase.

Ich kann mir nicht helfen, aber ich nehme an, dass ich etwas offensichtlich vermisse, da ich mir nicht vorstellen kann, dass dies kein ziemlich gewöhnliches Szenario ist.

Antwort

3

Ich denke, dass das Problem könnte sein, dass Ihre JSON doppelt serialisiert ist, wie die gestrichenen Anführungszeichen \", die sichtbaren Zeilenumbrüche \r\n und die Tatsache, dass der gesamte JSON zitiert wird. Wenn Sie doppelt serialisiertes JSON deserialisieren, ist das Ergebnis ein String, kein Objekt, das eindeutig nicht in ein IEnumerable<T> beliebiger Art konvertiert werden kann. Daher der Fehler.

Wenn Sie die Quelle des JSON steuern, stellen Sie sicher, dass Sie Ihre Objekte nur einmal serialisieren. Eine häufige Ursache des Problems liegt darin, dass Sie nicht erkennen, dass einige Frameworks wie die Web-API die Serialisierung automatisch für Sie durchführen. Wenn Sie Ihre Objekte also erst manuell serialisieren, bevor Sie sie zurückgeben, erhalten Sie zweifach serialisierte JSON.

Wenn Sie die Quelle des JSON nicht kontrollieren und/oder Sie den Eigentümer nicht dazu bringen können, den Fehler zu beheben, können Sie ihn auf der Clientseite korrigieren, indem Sie den JSON zweimal deserialisieren: JSON-String, dann zweite, um Ihre Objekte daraus zu erzeugen. Zum Beispiel:

string escapedJson = response.Content.ReadAsStringAsync().Result; 

string realJson = JsonConvert.DeserializeObject<string>(escapedJson); 

IEnumerable<MonitorBase> monitors = 
    JsonConvert.DeserializeObject<IEnumerable<MonitorBase>>(realJson, 
     new JsonSerializerSettings 
     { 
      TypeNameHandling = TypeNameHandling.Auto 
     }); 

EDIT

ich aus Ihrem Kommentar sehen, die Sie manuell Ihr Objekt um serialisieren zu Verwendung von Json.Net der TypeNameHandling Einstellung vorzunehmen, und Sie suchen nach einer Möglichkeit, vermeiden Sie die doppelte Serialisierung.

Sie haben grundsätzlich zwei Möglichkeiten:

  1. Fügen Sie die TypeNameHandling Einstellung zur globalen Serialisierung-Einstellungen des Web-API und entfernen Sie die manuelle Serialisierungscode aus Ihrer Methode. Fügen Sie dazu in Ihrer global.asax in der Application_Start() Methode Folgendes hinzu.

    var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings; 
    settings.TypeNameHandling = TypeNameHandling.Auto; 
    
  2. Alternativ können Sie Ihre Web-API-Methode ändern zu erstellen und eine HttpResponseMessage mit einem StringContent Objekt zurückgeben Ihre JSON und mit dem mediaType Satz zu application/json enthält. Dies sollte verhindern, dass Web API versucht, das Ergebnis erneut zu serialisieren.

    string json = JsonConvert.SerializeObject(monitors, new JsonSerializerSettings 
    { 
        TypeNameHandling = TypeNameHandling.Auto, 
        Formatting = Formatting.Indented 
    }); 
    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK); 
    response.Content = new StringContent(json, Encoding.UTF8, "application/json"); 
    return response; 
    
+0

Dank Brian. Ich kontrolliere beide Seiten des Prozesses und verwende Web-API. Ich werde die Daten jedoch nicht manuell serialisieren, daher muss ich herausfinden, warum die Doppelserialisierung stattfindet, aber was Sie sagen, macht Sinn. – chad

+0

Ok, ich denke, das Problem hängt mit der Tatsache zusammen, dass ich die TypeNameHandling-Einstellungen von Json.Net verwenden muss, um die $ type-Informationen in den serialisierten json zu bekommen. Also, ich führe ein JsonConvert.SerializeObject mit TypeNameHandling-Einstellungen aus und gebe dann diese Zeichenfolge zurück. Die Web-API scheint den JSON noch einmal zu serialisieren. Gibt es eine Möglichkeit, dies zu verhindern? Ich habe nichts Offensichtliches gefunden. – chad

+0

Ich habe meine Antwort mit einigen weiteren Informationen aktualisiert, die helfen sollten, das Problem zu lösen. –