【问题标题】:Deserialize json into C# object for class which has default private constructor将 json 反序列化为具有默认私有构造函数的类的 C# 对象
【发布时间】:2014-06-16 12:27:56
【问题描述】:

我需要为下面的课程反序列化 json。

public class Test
{
    public string Property { get; set; }

    private Test()
    {
        //NOTHING TO INITIALIZE
    }

    public Test(string prop)
    {
        Property = prop;
    }
}

我可以创建一个像这样的测试实例

var instance = new Test("Instance");

考虑到我的json之类的

"{  "Property":"Instance" }"

我应该如何创建一个 Test 类的对象,因为我的默认构造函数是私有的并且我正在获取 Property 为 NULL 的对象

我正在使用 Newtonsoft Json 解析器。

【问题讨论】:

  • 你用的是什么库?你如何获得Test 的实例?
  • 如果没有任何工厂可以生成 Test 对象,您希望如何将 JSON 反序列化为它的实例?

标签: c# json serialization json.net


【解决方案1】:

您可以通过使用[JsonConstructor] 属性标记来让 Json.Net 调用私有构造函数:

[JsonConstructor]
private Test()
{
    //NOTHING TO INITIALIZE
}

请注意,在调用构造函数后,序列化程序仍将使用公共 setter 来填充对象。


另一个可能的选择是使用ConstructorHandling 设置:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};

Test t = JsonConvert.DeserializeObject<Test>(json, settings);

【讨论】:

  • 如果您要序列化的对象位于您不想引入第三方依赖项的库/程序集中(包括对 Json.Net 的依赖项...),则第二个选项是最佳选择跨度>
  • 序列化器如何调用私有构造函数?我是否遗漏了什么,或者是JsonConstructor 指定在反序列化时调用哪个构造函数(对于具有多个公共构造函数的类),而不是能够神奇地调用私有构造函数?
  • [JsonConstructor] 告诉 Json.Net 在反序列化该类时使用哪个构造函数。序列化程序可以调用私有构造函数,因为它在内部使用reflection。通过反射可以调用私有方法。
【解决方案2】:

您似乎不需要采取任何额外的步骤。

使用 Json.NET v6.0.8,以下 C# 程序在 LINQPad 中运行:

void Main()
{   
    var o = JsonConvert.DeserializeObject<Test>("{\"Property\":\"Instance\"}");

    Debug.Assert(o.Property == "Instance",
        "Property value not set when deserializing.");
}

public class Test
{
    public string Property { get; set; }

    private Test()
    {
    }

    public Test(string propertyValue)
    {
        Property = propertyValue;
    }
}

【讨论】:

  • 网上稍作修改的例子:dotnetfiddle.net/4qFumV
  • 请注意,在此示例中,未使用带有 propertyValue 参数的公共构造函数。相反,私有构造函数与Propertysetter 一起使用。如果您想让它与 get only 属性一起使用,请参阅我对这个问题的回答。
【解决方案3】:

无需创建 Serializer 设置并在此处分配 ConstructorHandling。请记住将[JsonConstructor] 属性定义为私有构造函数。 我对抽象 BaseNode.cs 及其具体的 ComputerNode.cs 实现有类似的情况。您可以创建类,复制/粘贴下面的代码并进行一些实验。

    public abstract class BaseNode
{
    [JsonConstructor] // ctor used when Json Deserializing
    protected BaseNode(string Owner, string Name, string Identifier)
    {
        this.Name = Name;
        this.Identifier = Identifier;
    }

    // ctor called by concrete class.
    protected BaseNode(string [] specifications)
    {
        if (specifications == null)
        {
            throw new ArgumentNullException();
        }
        if (specifications.Length == 0)
        {
            throw new ArgumentException();
        }

        Name = specifications[0];
        Identifier = specifications[1];

    }

    public string Name{ get; protected set; }
    public string Identifier { get; protected set; }

}


public class ComputerNode: BaseNode
{
    public string Owner { get; private set; }

    [JsonConstructor] // not visible while creating object from outside and only used during Json Deserialization.
    private ComputerNode(string Owner, string Name, string Identifier):base(Owner, Name, Identifier)
    {
        this.Owner = Owner;
    }

    public ComputerNode(string[] specifications):base(specifications)
    {
        Owner = specifications[2];
    }
}

对于 JSON 读写以下代码有帮助 -

    public class Operation<T>
{
    public string path;

    public Operation()
    {
        var path = Path.Combine(Directory.GetCurrentDirectory(), "nodes.txt");

        if (File.Exists(path) == false)
        {
            using (File.Create(path))
            {
            }
        }
        this.path = path;
    }

    public void Write(string path, List<T> nodes)
    {
        var ser = JsonConvert.SerializeObject(nodes, Formatting.Indented);

        File.WriteAllText(path, ser);
    }

    public List<T> Read(string path)
    {
        var text = File.ReadAllText(path);

        var res =  JsonConvert.DeserializeObject<List<T>>(text);
        return res;
    }

}

一切顺利!

【讨论】:

    【解决方案4】:

    今天的简短回答是:将构造函数参数prop 重命名为property,您的代码就可以正常工作了。

    public class Test
    {
        public string Property { get; }
    
        public Test(string property)
        {
            Property = property;
        }
    }
    
    Console.WriteLine(
      JsonConvert.DeserializeObject(new Test("Instance")));
    
    

    Newtonsoft.Json 支持使用开箱即用的构造函数参数初始化属性,无需设置任何其他属性或更改任何设置。唯一的限制是参数名称需要与属性名称不区分大小写。

    【讨论】:

      【解决方案5】:

      我今天发现,有一个 public 构造函数接受参数 并且没有 声明 未参数化的构造函数导致 NewtonSoft尝试调用唯一可以找到的公共构造函数,因为没有显式的默认构造函数,并且它显然无法找到并调用框架提供的默认构造函数除非它是 唯一的构造函数

      显式声明默认构造函数会导致 NewtonSoft 调用正确的(未参数化的)构造函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-05
        • 1970-01-01
        • 2022-09-28
        • 1970-01-01
        • 1970-01-01
        • 2016-07-07
        • 2018-08-18
        • 1970-01-01
        相关资源
        最近更新 更多