JsonConvert.DeserializeObject (stringa) restituisce un valore nullo per la proprietà $ id

Sto scaricando il JSON utilizzando System.Net.WebClient.DownloadString. Sto ricevendo una risposta valida:

{ "FormDefinition": [ { "$id":"4", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Punchworks Form" }, { "$id":"6", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Punchworks Form test second" }, { "$id":"46", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"any_Name" }, { "$id":"47", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Punchworks Form test second" }, { "$id":"49", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Testing Name ??´????? ???? ACEeiÅ¡uu { [ ( ~ ! @ # " }, { "$id":"50", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"something new" }, { "$id":"56", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Testing Name руÌÑÑкий 汉è¯æ¼¢èªž ĄČĘėįšųū { [ ( ~ ! @ # " }, { "$id":"57", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Test Name" }, { "$id":"58", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 12:59:29 PM" }, { "$id":"59", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:01:18 PM" }, { "$id":"60", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:40:44 PM" }, { "$id":"61", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:43:46 PM" }, { "$id":"62", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:48:21 PM" }, { "$id":"63", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:57:00 PM" }, { "$id":"64", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:57:53 PM" }, { "$id":"65", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Unique Name - 12/16/2013 1:58:46 PM" }, { "$id":"79", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Testing Name1211" }, { "$id":"80", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Testing Name1211" }, { "$id":"81", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"any_nami" }, { "$id":"90", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Test_something3" }, { "$id":"91", "Class":558, "ClassDisplayLabel":"Punchworks", "Name":"Test_something4" }] } 

Ed ecco il mio modello:

 public class FormDefinitionList { [JsonProperty("FormDefinition")] public List FormDefinitions { get; set; } } public class FormDefinition { [JsonProperty ("$id")] public string Id { get; set; } [JsonProperty ("Class")] public int Class { get; set; } [JsonProperty ("ClassName")] public string ClassName { get; set; } [JsonProperty ("ClassDisplayLabel")] public string ClassDisplayLabel { get; set; } [JsonProperty ("Definition")] public string Definition { get; set; } [JsonProperty ("Name")] public string Name { get; set; } } 

Tutto funziona quando faccio:

 string response = "json as above"; FormDefinitionList root = JsonConvert.DeserializeObject (response); 

tranne che la proprietà Id ($ id) è sempre nullo. All’inizio ho cercato di capire se il simbolo del simbolo del dollaro che stavo tornando dal server era diverso, ma non sembra essere il caso. Non sono sicuro di dove andare da qui, quindi qualche idea?

Grazie in anticipo.

NOTA: Se provo a deserializzare con qualcosa come JavaScriptSerializer, funziona perfettamente, quindi sono abbastanza sicuro che sia qualcosa di sbagliato con il mio modello o con JSON.net. Potrebbe essere sbagliato però.

Json.Net normalmente usa $id insieme a $ref come metadati per conservare i riferimenti agli oggetti in JSON. Quindi, quando vede $id , presuppone che la proprietà non faccia parte dell’attuale insieme di proprietà JSON, ma di un identificatore interno. Quindi non popola la proprietà Id sul tuo object, anche se hai incluso un attributo [JsonProperty] che indica che dovrebbe.

AGGIORNARE

A partire dalla versione 6.0.4 di Json.Net, esiste una nuova impostazione in base alla quale è ansible indicare al deserializzatore il trattamento di queste proprietà “metadati” come proprietà normali anziché consumarle. Tutto quello che devi fare è impostare l’impostazione MetadataPropertyHandling su Ignore e poi deserializzare come al solito.

 var settings = new JsonSerializerSettings(); settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore; var obj = JsonConvert.DeserializeObject(json, settings); 

Prima della versione 6.0.4, era necessaria una soluzione alternativa per risolvere questo problema. Il resto di questa risposta discute i possibili soluzioni alternative. Se utilizzi la versione 6.0.4 o successiva, non hai bisogno della soluzione alternativa e puoi smettere di leggere ora.


La soluzione più semplice che riesco a vedere è di eseguire una sostituzione di stringa di "$id" con "id" (comprese le virgolette) sul JSON prima di deserializzarlo, come suggerito da @Carlos Coelho. Dato che dovresti farlo con ogni risposta, se segui questa strada ti consiglio di creare un semplice metodo di supporto per evitare la duplicazione del codice, ad esempio:

 public static T Deserialize(string json) { return JsonConvert.DeserializeObject(json.Replace("\"$id\"", "\"id\"")); } 

Tuttavia, dal momento che hai detto nei tuoi commenti che non sei così appassionato dell’idea di utilizzare una stringa sostitutiva, ho esaminato altre opzioni. Ho trovato un’altra alternativa che potrebbe funzionare per te, un JsonConverter personalizzato. L’idea alla base del convertitore è che cercherà di utilizzare i meccanismi di deserializzazione incorporati di Json.Net per creare e popolare l’object (sans ID), quindi recuperare manualmente la proprietà $id da JSON e utilizzarla per popolare la proprietà Id su l’object tramite riflessione.

Ecco il codice per il convertitore:

 public class DollarIdPreservingConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(FormDefinition); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); object o = jo.ToObject(objectType); JToken id = jo["$id"]; if (id != null) { PropertyInfo prop = objectType.GetProperty("Id"); if (prop != null && prop.CanWrite && prop.PropertyType == typeof(string)) { prop.SetValue(o, id.ToString(), null); } } return o; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Ho provato a scrivere il convertitore in modo tale che avrebbe funzionato per qualsiasi object che ha $id – hai solo bisogno di cambiare il metodo CanConvert conseguenza in modo che ritorni vero per tutti i tipi che devi usare per oltre a FormDefinition .

Per utilizzare il convertitore, devi solo passare un’istanza di esso a DeserializeObject questo modo:

 FormDefinitionList root = JsonConvert.DeserializeObject( json, new DollarIdPreservingConverter()); 

Nota importante: potresti essere tentato di decorare le tue classi con un attributo JsonConverter invece di far passare il convertitore nella chiamata DeserializeObject , ma non farlo: farà sì che il convertitore entri in un ciclo ricorsivo finché lo stack non trabocca. (C’è un modo per far funzionare il convertitore con l’attributo, ma dovresti riscrivere il metodo ReadJson per creare manualmente l’object di destinazione e popolare le sue proprietà invece di chiamare jo.ToObject(objectType) . È fattibile, ma un po ‘più disordinato.)

Fammi sapere se questo funziona per te.

Il problema è il segno $, quindi una soluzione alternativa sarebbe:

Rimuovi $ dall’annotazione JsonProperty.

 [JsonProperty ("id")] public string Id { get; set; } 

Sul tuo codice, sostituisci il carattere speciale $

 string response = "json as above"; FormDefinitionList root = JsonConvert.DeserializeObject (response.Replace("$id","id")); 

Modificato come suggerisce @BrianRogers

Questa risposta ha risolto il problema $ id / $ ref per me: Json.Net aggiungendo $ id agli oggetti EF nonostante l’impostazione di PreserveReferencesHandling su “None”

Nella tua implementazione di DefaultContractResolver / IContractResolver, aggiungi questo;

 public override JsonContract ResolveContract(Type type) { var contract = base.ResolveContract(type); contract.IsReference = false; return contract; } 

EDIT: questo rimuoverà $ id.