微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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(例如JObjectJValue)的子类,其具有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).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


原文地址:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx 原文发布日期: 9/19/2005 原文已经被 Microsoft 删除了,收集过程中发现很多文章图都不全,那是因为原文的图都不全,所以特收集完整全文。 目录 前言 CLR启动程序
前言 随着近些年微服务的流行,有越来越多的开发者和团队所采纳和使用,它的确提供了很多的优势也解决了很多的问题,但是我们也知道也并不是银弹,提供优势的同时它也给我们的开发人员和团队也带来了很多的挑战。 为了迎接或者采用这些新技术,开发团队需要更加注重一些流程或工具的使用,这样才能更好的适应这些新技术所
最近因为比较忙,好久没有写博客了,这篇主要给大家分享一下PLINQ中的分区。上一篇介绍了并行编程,这边详细介绍一下并行编程中的分区和自定义分区。 先做个假设,假设我们有一个200Mb的文本文件需要读取,怎么样才能做到最优的速度呢?对,很显然就是拆分,把文本文件拆分成很多个小文件,充分利用我们计算机中
在多核CPU在今天和不久的将来,计算机将拥有更多的内核,Microsoft为了利用这个硬件特性,于是在Visual Studio 2010 和 .NET Framework 4的发布及以上版本中,添加了并行编程这个新特性,我想它以后势必会改变我们的开发方式。 在以前或者说现在,我们在并行开发的时候可
c语言输入成绩怎么判断等级
字符型数据在内存中的存储形式是什么
c语言怎么求字符串的长度并输出
c语言函数的三种调用方式是什么
c语言中保留两位小数怎么表示
double的输入格式符是什么