1

I have an ASP.NET Core web API project targeting .NET Core 3.0 with the following controller:

public class FooController : ControllerBase
{
    [HttpPost]
    public ActionResult Post(Foo foo) => Ok()
}

Foo is defined in a separate library as:

public struct Foo
{
    public int Bar { get; }

    public Foo(int bar) => Bar = bar;
}

I call the API from a console app with:

new HttpClient().PostAsJsonAsync("http://localhost:55555/api/foo", new Foo(1)).Wait();

When the controller method is entered, foo.Bar has the default value of 0. I expect it to be 1.

This used to work as expected in .NET Core 2.2. The JSON deserializer handles properties with private setters on structs via an overloaded constructor with parameter names matching the property names (case-insensitive).

This no longer work in .NET Core 3.0 with basic structs (EDIT: due to this as pointed out by Martin Ullrich). However, if I use a standard struct type such as DateTime, it works fine. Is there something additional I must now do to my struct that DateTime for instance already supports? I've already tried implementing ISerializable on Foo with the code below, but that didn't work.

public Foo(SerializationInfo info, StreamingContext context)
{
    Bar = (int)info.GetValue("bar", typeof(int));
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("bar", Bar, typeof(int));
}

Any help would be greatly appreciated.

Neo
  • 4,145
  • 6
  • 53
  • 76

1 Answers1

1

The new System.Text.Json APIs do not support all the features that Newtonsoft.Json ("Json.NET") does, including deserialisation of read-only properties.

If you need this feature, switch to using Newtonsoft.Json as described in the Migrate from ASP.NET Core 2.2 to 3.0 Guide:

services.AddMvc()
    .AddNewtonsoftJson();

or

services.AddControllers()
    .AddNewtonsoftJson();

DateTime is already known by the System.Text.Json stack in 3.0 and there also is a JsonConverter<T> implementation for it: JsonConverterDateTime.

For creating custom converters and registering them for ASP.NET Core, see https://stackoverflow.com/a/57334833/784387

Martin Ullrich
  • 94,744
  • 25
  • 252
  • 217
  • Thanks - this is a great answer, and may do as a temporary workaround till I resolve it, but doesn't quite answer the question I asked, which was, is there something additional I must now do to my struct that `DateTime` for instance already supports given that `DateTime` works fine and also has readonly properties? – Neo Oct 08 '19 at 20:36
  • 1
    Edited to include details on converters. – Martin Ullrich Oct 08 '19 at 22:03
  • OK, thanks for the info. Given there's an issue and a [PR](https://github.com/dotnet/corefx/pull/40517) open for this which could mean the issue would be resolved with a simple attribute on the readonly property, I'll hold off for that for now and continue to use the `AddNewtonsoftJson` workaround as creating a custom converter is overkill and may not be required if the aforementioned PR is approved. – Neo Oct 08 '19 at 22:24