Json.Net中的私有setter方法

112

我知道有一个属性可以处理私有的setter,但我想默认情况下采用这种行为,有没有办法实现这一点?除了调整源代码以外。如果有设置可以做到这点就太好了。


1
我正在寻找这个或者那个答案。 - marbel82
5个回答

143

我来到这里是为了寻找让Json.NET在反序列化时填充只读属性的实际属性,那就是简单的[JsonProperty],例如:

我来到这里是为了寻找使 Json.NET 反序列化时填充只读属性的实际属性,这个属性就是 [JsonProperty],例如:

[JsonProperty]
public Guid? ClientId { get; private set; }

替代方案

只需提供一个构造函数,其参数匹配您的属性:

public class Foo
{
    public string Bar { get; }

    public Foo(string bar)
    {
        Bar = bar;
    }
}

现在这个可以运行:

string json = "{ \"bar\": \"Stack Overflow\" }";

var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow

如果可能的话,我更喜欢这种方法,因为:

  • 它不需要你在属性上添加属性。
  • 它适用于{ get; private set; }和只有{ get; }的情况。

27
只是一个小提示:它可以使用 {get;private set;},而不能使用 {get;} - tymtam
12
更新一下,它现在也适用于 {get;}。 - Hav
1
@Hav 这是哪个版本?我刚刚测试了v11.0.2,它 不能 工作 {get;} - tymtam
1
@tymtam我在官方文档中没有找到任何详细的内容。但是通过查看代码,反序列化的工作方式是首先使用非默认构造函数(如果有),将构造函数参数名称与属性名称匹配来实例化对象。然后进行另一次遍历,设置无法在构造函数中设置的属性。因此,对于具有{get;} 的属性,它唯一的机会就是使用构造函数,因此您需要提供一个具有匹配参数名称的构造函数。请参阅此答案以获取更多详细信息。 - Saeb Amini
2
@tymtam更新了答案,并提供了这个替代方案和一个示例。 - Saeb Amini
显示剩余4条评论

92

更新, 新答案

我为此编写了一个源分发NuGet,它安装了一个包含两个自定义合同解析器的单个文件:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

安装NuGet软件包:

Install-Package JsonNet.ContractResolvers

那么只需使用任意一个解析器:

var settings = new JsonSerializerSettings
{
    ContractResolver = new PrivateSetterContractResolver()
};

var model = JsonConvert.DeserializeObject<Model>(json, settings);

你可以在这里阅读相关信息:http://danielwertheim.se/json-net-private-setters-nuget/

GitHub仓库链接: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers

旧答案(仍然有效)

有两个替代方案可以解决这个问题。

替代方案1:对反序列化器进行设置

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

默认序列化选项支持所有类型的类成员。因此,这个解决方案将返回所有私有成员类型,包括字段。我只对支持私有 setter 的内容感兴趣。

备选方案2:创建自定义 ContractResolver:

因此,这是更好的选择,因为我们只检查属性。

public class SisoJsonDefaultContractResolver : DefaultContractResolver 
{
    protected override JsonProperty CreateProperty(
        MemberInfo member,
        MemberSerialization memberSerialization)
    {
        //TODO: Maybe cache
        var prop = base.CreateProperty(member, memberSerialization);

        if (!prop.Writable)
        {
            var property = member as PropertyInfo;
            if (property != null)
            {
                var hasPrivateSetter = property.GetSetMethod(true) != null;
                prop.Writable = hasPrivateSetter;
            }
        }

        return prop;
    }
}

查看更多信息,请阅读我的帖子:http://danielwertheim.se/json-net-private-setters/


2
将您的帖子链接连接到http://daniel.wertheim.se/2010/11/06/json-net-private-setters/。 - Jafin
1
@Jafin 的链接已失效,现在可以在 https://danielwertheim.wordpress.com/2010/11/06/json-net-private-setters/ 上找到它。 - Chris Marisic
1
看起来现在Alt 2绝对是最佳选择。DefaultMembersSearchFlags已被弃用 - Todd Menier
5
在C# 6中, {get;}并不等同于{ get; private set; }。对于第一种方式,property.GetSetMethod(true)返回值为null,而对于后者则为true。这让我感到惊讶。你必须要有private set;才能使反序列化按预期工作。 - emragins
1
现在似乎应该使用Install-Package JsonNet.ContractResolvers。https://github.com/danielwertheim/jsonnet-contractresolvers - AlignedDev

15

@Daniel的回答(Alt2)非常准确,但我需要它适用于私有setter和getter(我正在使用一个实际上有一些只写入事物的API,比如 user.password )。这是我最终得出的:

<code>public class NonPublicPropertiesResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        var prop = base.CreateProperty(member, memberSerialization);
        if (member is PropertyInfo pi) {
            prop.Readable = (pi.GetMethod != null);
            prop.Writable = (pi.SetMethod != null);
        }
        return prop;
    }
}
</code>

已注册如下:

<code>JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new NonPublicPropertiesResolver()
};
</code>

11
从C# 9开始,建议使用 Init Only Setters而不是私有setters来从JSON初始化对象。例如:public string Summary { get; init; } 如果您坚持使用私有setters,则需要使用JsonInclude属性注释此类属性。
无论哪种方式,JsonSerializer.DeserializeAsync都将反序列化属性。

2
[JsonInclude] 已经生效,谢谢! - bzmind

1

根据撰写此文章时的文档,以下是相关内容:

默认情况下,Json.NET首先寻找一个用JsonConstructorAttribute标记的构造函数,然后查找一个公共默认构造函数(不带任何参数的构造函数),然后检查该类是否具有单个带参数的公共构造函数,最后检查非公共默认构造函数。

这意味着如果您有默认构造函数(没有参数),那么它将优先于其他构造函数被选择。可以通过使用JsonConstructorAttribute装饰所需的构造函数来解决此问题。

另外需要注意的是- 使用JsonProperty装饰属性对我来说无论是否有构造函数都不起作用。


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