JsonConvert.DeserializeObject (string)为$ id属性返回null值

我正在使用System.Net.WebClient.DownloadString下载JSON。 我收到了有效回复:

{ "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" }] } 

这是我的模型:

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

我做的每件事都有效:

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

除了Id($ id)属性始终为null。 起初我试图弄清楚我从服务器回来的美元符号是不同的,但事实并非如此。 我不知道从哪里开始,所以任何想法?

提前致谢。

注意:如果我尝试使用JavaScriptSerializer之类的反序列化,它可以完美地工作,所以我很确定我的模型或JSON.net有问题。 可能是错的。

Json.Net通常使用$id$ref作为元数据来保存JSON中的对象引用。 因此,当它看到$id它假定该属性不是实际JSON属性集的一部分,而是内部标识符。 因此,它不会填充对象上的Id属性,即使您包含一个[JsonProperty]属性指示它应该。

UPDATE

从Json.Net版本6.0.4开始 ,有一个新设置可以指示反序列化器将这些“元数据”属性视为普通属性而不是消耗它们。 您需要做的就是将MetadataPropertyHandling设置为Ignore ,然后照常反序列化。

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

在6.0.4版之前,需要一种解决方法来解决此问题。 本答案的其余部分讨论了可能的解决方法。 如果您使用的是6.0.4或更高版本,则不需要解决方法,现在可以停止阅读。


我可以看到的最简单的解决方法是在反序列化之前在JSON上用"id" (包括引号)替换"$id" ,正如@Carlos Coelho建议的那样。 由于您必须对每个响应执行此操作,如果您采用此路由,我建议您使用简单的帮助方法来避免代码重复,例如:

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

但是,既然你在评论中说过你不是那么热衷于使用字符串替换的想法,我会研究其他选项。 我找到了另一个可能对你JsonConverter替代方案 – 一个自定义的JsonConverter 。 转换器背后的想法是它会尝试使用Json.Net的内置反序列化机制来创建和填充对象(无ID),然后从JSON手动检索$id属性并使用它来填充Id属性通过reflection的对象。

这是转换器的代码:

 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(); } } 

我尝试编写转换器,使其适用于任何具有$id对象 – 您只需要相应地更改CanConvert方法,以便除了FormDefinition之外, FormDefinition需要为其使用它的所有类型返回true 。

要使用转换器,只需将它的实例传递给DeserializeObject如下所示:

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

重要提示:您可能想要使用JsonConverter属性来装饰类,而不是将转换器传递给DeserializeObject调用,但不要这样做 – 它会导致转换器进入递归循环,直到堆栈溢出。 (有一种方法可以让转换器使用该属性,但你必须重写ReadJson方法来手动创建目标对象并填充其属性而不是调用jo.ToObject(objectType) 。这是可行的,但是更麻烦。)

如果这对您有用,请告诉我。

问题是$符号,因此解决方法是:

从JsonProperty注释中删除$。

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

在您的代码上,替换特殊字符$

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

编辑为@BrianRogers建议

这个答案为我修复了$ id / $ ref问题: 尽管将PreserveReferencesHandling设置为“None”,Json.Net会向EF对象添加$ id

在您的DefaultContractResolver / IContractResolver的实现中,添加此项;

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

编辑:这将删除$ id。