
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(例如JObject或JValue)的子类,其具有IDynamicMetaObjectProvider的自定义实现.
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的第二个晦涩的行为:而不是用空指针表示空值,它代表着一个特殊的JValue,JValue.Type等于JTokenType.Null.因此,如果我这样做:
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
为什么有区别?这是因为JValue返回的DynamicMetaObject将动态类型转换为字符串,最终调用ConvertUtils.Convert(value,CultureInfo.InvariantCulture,binder.Type),最终返回一个JTokenType.Null值的空值,这是由显式转换为字符串执行的相同逻辑,避免了动态的所有使用:
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).
总结以上是内存溢出为你收集整理的c# – Null-coalescing *** 作符为动态对象的属性返回null全部内容,希望文章能够帮你解决c# – Null-coalescing *** 作符为动态对象的属性返回null所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)