【问题标题】:Using Newtonsoft.Json with nested custom classes将 Newtonsoft.Json 与嵌套的自定义类一起使用
【发布时间】:2017-01-09 15:39:16
【问题描述】:

我需要序列化一些自定义对象以存储信息。但是,我正在努力将这些对象从序列化的 JSON 字符串反序列化回其原始对象形式。

序列化的字符串看起来不错:

[
  {
    "MyStringArray": [
      "stringInput1",
      "stringInput2"
    ],
    "MyCharArray": [
      "a",
      "b",
      "c",
      "."
    ],
    "MyString": "dummy",
    "MyClass3Object": [
      {
        "MyString": "ListInput1"
      },
      {
        "MyString": "ListInput2"
      }
    ]
  }
]

但是,当我重建原始 MyClass1 对象时,列表应该有一个条目,但它填充了空值而不是相应的数据。关于可能发生的事情有什么想法吗?提前感谢您的头脑风暴:)

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.IO;
using System.Text.RegularExpressions;

namespace JsonTesting
{
  class Program
  {
    static void Main(string[] args)
    {

      MyClass1 c1 = new MyClass1();
      c1.AddInfo();

      string toJsonString = JsonConvert.SerializeObject(c1, Formatting.Indented,
        new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Include });
      File.WriteAllText(@"C:\temp\dumpJsonText.txt",toJsonString);
      MyClass1 fromJson = JsonConvert.DeserializeObject<MyClass1>(toJsonString);
      Console.ReadLine();

    }
  }


  public class MyClass1 : List<MyClass2> {
    public MyClass1() { }
    public void AddInfo() {
      this.Add(new MyClass2(new string[] { "stringInput1", "stringInput2" },
        new char[] { 'a', 'b', 'c', '.' },
        "dummy",
        new List<MyClass3>() { new MyClass3("ListInput1", new Regex(@"[A-Z]")), new MyClass3("ListInput2", new Regex(@"[0-9]")) }
        ));
    }
  }

  public class MyClass2
  {
    private string[] _myStringArray = null;
    private char[] _myCharArray = null;
    private string _myString = null;
    private List<MyClass3> _myClass3Object = null;

    public MyClass2() { }

    public MyClass2(string[] myStringArray, char[] myCharArray, string myString, List<MyClass3> myClass3Object)
    {
      _myStringArray = myStringArray;
      _myCharArray = myCharArray;
      _myString = myString;
      _myClass3Object = myClass3Object;
    }

    public string[] MyStringArray { get { return _myStringArray; } }
    public char[] MyCharArray { get { return _myCharArray; } }
    public string MyString { get { return _myString; } }
    public List<MyClass3> MyClass3Object { get { return _myClass3Object; } }

  }

  public class MyClass3 {

    private Regex _myRegex; 
    private string _myString = null;
    public MyClass3() { }
    public MyClass3(string myString, Regex myRegex) {

      _myString = myString;
      _myRegex = myRegex;
    }

    public string MyString{ get {return _myString;} }

  }

}

【问题讨论】:

  • 你没有任何公共的空构造函数,所以它不知道如何创建嵌套对象。
  • 我在所有 3 个类中都有空构造函数。我只是忘了在这里添加它。
  • 您在问为什么非常具体的代码行为不正确,然后您发布了伪代码
  • 我已经完全按照我的项目编写代码,只是更改了名称并省略了不相关的方法
  • 提供一个minimal reproducible example 重现问题,以便得出更好的答案

标签: c# json serialization json.net


【解决方案1】:

您的课程MyClass2MyClass3 是只读的。为了让 Json.NET 反序列化只读类型,您必须提供 custom JsonConverter 手动反序列化并构造该类型的实例,或者提供参数化构造函数,其 参数名称与属性名称取模大小写匹配。您已经创建了必要的构造函数,所以已经完成了一半。

但是,您的类型也有无参数构造函数。那么,Json.NET 调用了哪个构造函数呢?对于序列化为 JSON object 的不可枚举类型,适用以下规则:

  1. 如果在构造函数上设置了[JsonConstructor],则使用该构造函数。

  2. 接下来,在full trust only中,当应用MemberSerialization.Fields,或者应用[Serializable]DefaultContractResolver.IgnoreSerializableAttribute == false时,使用特殊方法FormatterServices.GetUninitializedObject()分配对象。 没有调用该类型的构造函数。

    (这是一种不常见的情况,并不经常出现。)

  3. 接下来,如果有公共的无参数构造函数,使用它。

  4. 接下来,如果存在私有无参数构造函数并且设置ConstructorHandling.AllowNonPublicDefaultConstructor 启用时,使用私有无参数构造函数。

  5. 接下来,如果有单个公共参数化构造函数,则使用该构造函数。

  6. 如果上述所有条件都失败,Json.NET 无法构造该类型的实例。除非自定义转换器可用,否则反序列化期间将引发异常。

因此,无参数构造函数优先于有参数构造函数。要强制使用参数化构造函数,请使用[JsonConstructor] 标记它们,如上所述:

public class MyClass3
{
    private Regex _myRegex;
    private string _myString = null;

    public MyClass3() { }

    [JsonConstructor]
    // The argument names must match the property names modulo case for Json.NET to deserialize the properties successfully.
    public MyClass3(string myString, Regex myRegex)
    {
        _myString = myString;
        _myRegex = myRegex;
    }

    public string MyString { get { return _myString; } }

    public Regex MyRegex { get { return _myRegex; } }
}

或者,您可以消除无参数构造函数,因为它在您的问题的第一个版本中显然不存在。然后对MyClass2 进行相同的更改。现在您的类型将成功反序列化。

请注意,Json.NET 有一个 built-in converter 用于序列化 Regex

示例fiddle

【讨论】:

  • 我在 MyClass2 和 MyClass3 中添加了 [JsonConstructor],它可以工作 :) 感谢您提供如此明确的答案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-08
  • 2011-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
相关资源
最近更新 更多