Private setters in Json.Net
I know there's an attribute to handle private setters but I kind of want this behavior as a default, is there a way to accomplish this? Except tweaking the source. Would be gr开发者_Go百科eat if there was a setting for this.
I came here looking for the actual attribute that makes Json.NET populate a readonly property when deserializing, and that's simply [JsonProperty]
, e.g.:
[JsonProperty]
public Guid? ClientId { get; private set; }
Alternative Solution
Just provide a constructor that has a parameter matching your property:
public class Foo
{
public string Bar { get; }
public Foo(string bar)
{
Bar = bar;
}
}
Now this works:
string json = "{ \"bar\": \"Stack Overflow\" }";
var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow
I prefer this approach where possible since:
- It doesn't require you to decorate your properties with attributes.
- It works with both
{ get; private set; }
and just{ get; }
.
Updated, new answer
I've written a source distribution NuGet for this, that installs a single file with two custom contract resolvers:
- PrivateSetterContractResolver
- PrivateSetterCamelCasePropertyNamesContractResolver
Install the NuGet package:
Install-Package JsonNet.ContractResolvers
Then just use any of the resolvers:
var settings = new JsonSerializerSettings
{
ContractResolver = new PrivateSetterContractResolver()
};
var model = JsonConvert.DeserializeObject<Model>(json, settings);
You can read about it here: http://danielwertheim.se/json-net-private-setters-nuget/
GitHub repo: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers
Old answer (still valid)
There are two alternatives that can solve the problem.
Alt 1: On the deserializers
ContractResolver.DefaultMembersSearchFlags =
DefaultMembersSearchFlags | BindingFlags.NonPublic;
The default serialization option supports all types of class member. Therefore this solution will return all private members types including fields. I'm only interested in also supporting private setters.
Alt2: Create a custom ContractResolver:
Therefore this is the better options since we just check the properties.
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;
}
}
For more information, read my post: http://danielwertheim.se/json-net-private-setters/
@Daniel's answer (Alt2) is spot on, but I needed this to work for both private setters and getters (I'm working with an API that actually has a few write-only things, like user.password
.) Here's what I ended up with:
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;
}
}
Registered thusly:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
ContractResolver = new NonPublicPropertiesResolver()
};
Starting from C# 9 it's recommended to use Init Only Setters rather than private setters when initialising an object from JSON. E.g. public string Summary { get; init; }
If you insist on private setters, then you'd need to annotate such properties with JsonInclude attribute.
Either way, JsonSerializer.DeserializeAsync will deserialise the properties.
What the docs have to say about it as per the moment of writing this post:
By default Json.NET will first look for a constructor marked with the JsonConstructorAttribute, then look for a public default constructor (a constructor that doesn't take any arguments), then check if the class has a single public constructor with arguments and finally check for a non-public default constructor.
Goes to say that if you have default constructor (with no arguments) then that will be selected over any other constructors. This can be solved by decorating the desired constructor with JsonConstructorAttribute
.
On a separate note - decorating properties with JsonProperty
does not work for me both with and without constructor.
精彩评论