111

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 great if there was a setting for this.

Saeb Amini
  • 23,054
  • 9
  • 78
  • 76
Daniel
  • 8,133
  • 5
  • 36
  • 51
  • 1
    I was looking for [this](https://stackoverflow.com/a/32010248/2122718) or [that](https://stackoverflow.com/a/39380844/2122718) answer. – marbel82 Sep 04 '17 at 09:16

5 Answers5

142

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; }.
Saeb Amini
  • 23,054
  • 9
  • 78
  • 76
  • 27
    Just a tiny note: it works with `{get;private set;}`, not with `{get;}` – tymtam Aug 29 '17 at 01:40
  • 12
    Just a little update. Now it also works with {get;}; – Hav Aug 10 '18 at 11:28
  • 1
    @Hav What version is it since? I've just tested v11.0.2 and it does **not** work {get;} – tymtam Sep 06 '18 at 01:16
  • 1
    @tymtam I think it only works with `{ get; }` if the type has a constructor with a parameter matching the property name. – Saeb Amini Sep 06 '18 at 03:27
  • @SaebAmini Could you find the documentation or example for this? – tymtam Sep 06 '18 at 03:30
  • 1
    @tymtam I haven't been able to find anything in the official doco going to this level of detail. But looking at the code, the deserialization works by first instantiating the object using a nondefault constructor (if any), matching constructor parameter names to property names. It then makes another pass setting properties it couldn't in the constructor. So for a property with `{ get; }`, its only chance would be to use the constructor therefore you need to provide one with a matching parameter name. Have a look at [this answer](https://stackoverflow.com/a/33182520/68080) for some more detail. – Saeb Amini Sep 06 '18 at 03:51
  • 2
    @tymtam updated the answer with this alternative and an example. – Saeb Amini Sep 06 '18 at 04:10
  • Note that the property name `Bar` must match the constructor parameter name `bar` case insensitive. – huang Nov 06 '19 at 14:46
  • This is right solution it work with {get; private set;} tested in .net 5 – Yogesh Apr 18 '21 at 05:47
90

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/

TanvirArjel
  • 30,049
  • 14
  • 78
  • 114
Daniel
  • 8,133
  • 5
  • 36
  • 51
  • 2
    Linking to your post http://daniel.wertheim.se/2010/11/06/json-net-private-setters/ – Jafin Apr 21 '11 at 02:15
  • 1
    @Jafin url is dead, https://danielwertheim.wordpress.com/2010/11/06/json-net-private-setters/ has it now – Chris Marisic Jan 05 '15 at 17:28
  • 1
    Looks like **Alt 2** is definitely the way to go nowadays. `DefaultMembersSearchFlags` has been [deprecated](http://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_DefaultContractResolver_DefaultMembersSearchFlags.htm). – Todd Menier Jun 29 '15 at 18:45
  • 5
    With c# 6, `{get; }` is NOT equivalent to `{ get; private set; }`. For the first way `property.GetSetMethod(true)` returns `null` and the latter `true`. This surprised me. You must have `private set;` for deserialization to work as expected. – emragins May 07 '16 at 04:00
  • 1
    It looks like Install-Package JsonNet.ContractResolvers should be used now. https://github.com/danielwertheim/jsonnet-contractresolvers – AlignedDev Dec 03 '19 at 20:49
14

@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()
};
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
8

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.

Alex Klaus
  • 8,168
  • 8
  • 71
  • 87
1

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.

Alex
  • 715
  • 1
  • 8
  • 29