c# – Null-coalescing操作符为动态对象的属性返回null
我最近在使用Json.NET来解析
JSON作为动态对象时,最近发现了一个null-coalescing操作符的问题.假设这是我的动态对象:
string json = "{ "phones": { "personal": null },"birthday": null }"; dynamic d = JsonConvert.DeserializeObject(json); 如果我尝试使用?操作符在d的一个字段上,它返回null: string s = ""; s += (d.phones.personal ?? "default"); Console.WriteLine(s + " " + s.Length); //outputs 0 但是,如果我为动态属性分配一个字符串,那么它可以正常工作: string ss = d.phones.personal; string s = ""; s += (ss ?? "default"); Console.WriteLine(s + " " + s.Length); //outputs default 7 最后,当我输出Console.WriteLine(d.phones.personal == null)时,它输出True. 我在Pastebin年对这些问题进行了广泛的考察. 解决方法
这是由于Json.NET的模糊行为和操作符.
首先,当您将JSON反序列化为动态对象时,实际返回的是Linq-to-JSON类型JToken(例如 dynamic d1 = JsonConvert.DeserializeObject(json); var d2 = JsonConvert.DeserializeObject<JObject>(json); 实际上回来了同样的事情.所以,对于你的JSON字符串,如果我这样做 var s1 = JsonConvert.DeserializeObject<JObject>(json)["phones"]["personal"]; var s2 = JsonConvert.DeserializeObject<dynamic>(json).phones.personal; 这两个表达式都可以计算出完全相同的返回的动态对象.但是什么对象被返回?这让我们得到了Json.NET的第二个晦涩的行为:而不是用空指针表示空值,它代表着一个特殊的 WriteTypeAndValue(s1,"s1"); WriteTypeAndValue(s2,"s2"); 控制台输出是: "s1": Newtonsoft.Json.Linq.JValue: "" "s2": Newtonsoft.Json.Linq.JValue: "" 即这些对象不为空,它们被分配给POCO,并且它们的ToString()返回一个空字符串. 但是,当我们将动态类型分配给字符串时会发生什么? string tmp; WriteTypeAndValue(tmp = s2,"tmp = s2"); 打印: "tmp = s2": System.String: null value 为什么有区别?这是因为 WriteTypeAndValue((string)JsonConvert.DeserializeObject<JObject>(json)["phones"]["personal"],"Linq-to-JSON with cast"); // Prints "Linq-to-JSON with cast": System.String: null value WriteTypeAndValue(JsonConvert.DeserializeObject<JObject>(json)["phones"]["personal"],"Linq-to-JSON without cast"); // Prints "Linq-to-JSON without cast": Newtonsoft.Json.Linq.JValue: "" 现在,到实际的问题.正如husterk所说?? operator返回动态时,其中一个操作数是动态的,所以d.phones.personal? “default”不会尝试执行类型转换,因此返回值为JValue: dynamic d = JsonConvert.DeserializeObject<dynamic>(json); WriteTypeAndValue((d.phones.personal ?? "default"),"d.phones.personal ?? "default""); // Prints "(d.phones.personal ?? "default")": Newtonsoft.Json.Linq.JValue: "" 但是,如果我们通过将动态返回值分配给字符串来调用Json.NET的类型转换,则转换器将在合并运算符完成其工作并返回非空值JValue之后启动并返回一个实际的空指针: string tmp; WriteTypeAndValue(tmp = (d.phones.personal ?? "default"),"tmp = (d.phones.personal ?? "default")"); // Prints "tmp = (d.phones.personal ?? "default")": System.String: null value 这解释了你所看到的差异. 要避免此行为,请在应用合并运算符之前强制将动态转换为字符串: s += ((string)d.phones.personal ?? "default"); 最后,帮助程序将类型和值写入控制台: public static void WriteTypeAndValue<T>(T value,string prefix = null) { prefix = string.IsNullOrEmpty(prefix) ? null : """+prefix+"": "; Type type; try { type = value.GetType(); } catch (NullReferenceException) { Console.WriteLine(string.Format("{0} {1}: null value",prefix,typeof(T).FullName)); return; } Console.WriteLine(string.Format("{0} {1}: "{2}"",type.FullName,value)); } (除此之外,null类型的JValue的存在解释了表达式(object)(JValue)(string)null ==(object)(JValue)null可能如何评估为false). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |