Json.NET,无法反序列化可空类型

23

我正在尝试使用Json.NET将JSON转换为C#对象。 在C#中,对象看起来像这样:

public class MyObject 
{
   public int? integerValue {get;set;}
   public DateTime? dateTimeValue {get;set;}
}

但是,当我对传入的JSON运行JsonConvert.DeserializeObject()时,我会得到以下异常:

找不到用于类型System.Nullable`1[System.Int32]的构造函数。一个类应该要么有一个默认构造函数,要么只有一个带参数的构造函数。

--- 编辑 ---

事实证明,在进行许多测试后,问题归结为我的JSON输入如下:

{integerValue:{}, dateTimeValue: {} }

改为:

{integerValue: null, dateTimeValue: null}

结果表明,在JSON中,使用 {} 表示空对象是有效的,但是 JSON.Net解析器在反序列化时未能将{}标记视为“null”的同义词。

感谢大家的参与!


你能添加你正在尝试反序列化的JSON吗? - user47589
我遇到了同样的问题,只不过是 {value:"null"} 而不是 {value:null}。 - craastad
我只想添加一个相关问题的链接,这个问题帮助我解决了同样的问题:https://dev59.com/CGw15IYBdhLWcg3wmc8J 添加:[JsonProperty(NullValueHandling=NullValueHandling.Ignore)] - Yogi
与Newtonsoft.Json 6.0.0 rv v4.0.30319存在相同的问题。 - user1005462
5个回答

18
错误提示说明它无法找到可用于反序列化的构造函数。
尝试向类中添加默认构造函数:
public class MyObject
{
    public int? integerValue { get; set; }
    public DateTime? dateTimeValue { get; set; }

    public MyObject(){}
} 

Patrick。

--EDIT--

所以我刚刚创建了一个简单的控制台应用程序,使用了你的MyObject,分别带有和不带有默认构造函数,但我没有收到任何错误。这是我的示例:

class Program
{
    static void Main(string[] args)
    {
        var mo = new MyObject { integerValue = null, dateTimeValue = null };
        var ser = Newtonsoft.Json.JsonConvert.SerializeObject(mo);
        var deser = Newtonsoft.Json.JsonConvert.DeserializeObject(ser, typeof(MyObject));
    }
}

public class MyObject
{
    public int? integerValue { get; set; }
    public DateTime? dateTimeValue { get; set; }        
}  

我收不到任何异常...

您能展示一下您尝试反序列化的JSON的示例吗?


1
Patrick,我认为事情并不像那么简单。错误不在于MyObject没有默认构造函数,而是说System.Nullable类型没有默认构造函数!每个用户定义的类都会由编译器隐式生成一个默认的无参构造函数,除非构造函数被标记为私有。 - 7wp
你说得对,无参构造函数是由编译器添加的。但在开发WCF REST服务(XML和JSON)时,我遇到过许多情况,如果没有明确添加一个无参数构造函数,就会出现序列化问题。因此理论上,你所说的应该是可行的,但在实践中,我的经验不同。我会尝试使用您拥有的相同工具设置一个项目,看看是否能够找到更好的选项。 - Patrick
2
感谢您的努力。我刚刚发现问题所在。原来我的JSON输入字符串中,对于空值使用了 {} 而不是 null。虽然在JSON中 {} 仍然是有效的,但是JSON.Net转换器无法像处理null一样处理 {}。 - 7wp
我会接受你的答案作为被采纳的答案,因为它是促使我找到解决方案的答案。 - 7wp
只是为了帮助那些可能忽略了属性访问修饰符的人 - 如果你指定了这个:public DateTime? dateTimeValue { get; private set; },由于保护级别,你最终将没有设置日期时间。当我发现这个问题时,我感到非常愚蠢,因为我盯着自己的代码看了几个小时... - Henning Winter

11

对我而言,解决方案是按照这个答案创建转换器。

public class BoolConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((bool)value) ? 1 : 0);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null || reader.Value.ToString() == "False")
        {
            return false;
        }
        return true;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(bool);
    }
}

然后在模型中指定

[JsonConverter(typeof(BoolConverter))]
public Boolean bold;

3
我不确定这是否是正确的答案,但至少您可以为Nullable<>创建自定义转换器,它在DataRow序列化/反序列化方面对我有很大帮助,而且它也没有默认构造函数。这里是一个示例:样例

Sergey,谢谢你的帮助。但事实证明问题是另外一回事。请看我发布的问题中的编辑。 - 7wp

0

同样要确保你的属性有公共的setter方法,以便反序列化工作。


0

Patrick答案的完整版本:

static void Main(string[] args)
{
    var mo = new MyObject ();
    var ser = Newtonsoft.Json.JsonConvert.SerializeObject(mo);
    var myStr = "{}";
    var myStr1 = "{tITi: 10}";
    var myStr2 = "{integerValue: 10}";
    var deser0 = Newtonsoft.Json.JsonConvert.DeserializeObject(ser, typeof(MyObject));
    var deser1 = Newtonsoft.Json.JsonConvert.DeserializeObject(myStr, typeof(MyObject));
    var deser2 = Newtonsoft.Json.JsonConvert.DeserializeObject(myStr1, typeof(MyObject));
    var deser3 = Newtonsoft.Json.JsonConvert.DeserializeObject(myStr2, typeof(MyObject));
}

public class MyObject
{
    public int? integerValue { get; set; }
    public DateTime? dateTimeValue { get; set; }
    public int toto { get; set;  } = 5;
    public int Titi;
}

输出:

?deser0
{ConsoleApplication1.MyObject}
    Titi: 0
    dateTimeValue: null
    integerValue: null
    toto: 5
?deser1
{ConsoleApplication1.MyObject}
    Titi: 0
    dateTimeValue: null
    integerValue: null
    toto: 5
?deser2
{ConsoleApplication1.MyObject}
    Titi: 10
    dateTimeValue: null
    integerValue: null
    toto: 5
?deser3
{ConsoleApplication1.MyObject}
    Titi: 0
    dateTimeValue: null
    integerValue: 10
    toto: 5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接