JsonPatchDocument is null after migration to .Net Core 3

Viewed 2007

I have a AspNetCore-WebApi-Project with several patch-operations, which worked fine with Core 2.2. After migration to Core 3 the [FromBody] JsonPatchDocument<T> is null. My Get/Post-Methods are still functioning as expected.

This is one part of my Startup:

    services.AddDbContext<MyContext>(options => options
                    .UseLazyLoadingProxies()
                    .UseNpgsql(Configuration.GetConnectionString("MyConnectionString"), 
                        opt => opt.UseNodaTime()));

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "My-API", Version = "v1" });
    });
    services.AddControllers()
        .AddNewtonsoftJson();

This is my Action:

[HttpPatch("{id}")]
public async Task<IActionResult> Patch(Guid id, 
                            [FromBody] JsonPatchDocument<MyViewModel> patchDocument)
{
    await this.service.HandlePatchAsync(id, patchDocument);
    return NoContent();
}

This is the body-content:

[   
    {
        "op": "replace",
        "path": "/name",
        "value": "New Name" 
    },
    {
        "op": "replace",
        "path": "/country",
        "value": "Germany" 
    }
]

Does anyone have an idea what is goung wrong here?

2 Answers

I struggle with a similar issue. I was going to get rid of Newtonsoft at all, but in that case the patch with JsonPatchDocument was not working.

According to https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#jsonnet-support you should:

  1. Add a package reference to Microsoft.AspNetCore.Mvc.NewtonsoftJson

  2. Change code in the startup adding MVC to services.AddMvc().AddNewtonsoftJson();

You did the second step, but what about the first? This helped me.

Unfortunatelly, I do not know how to make JsonPatchDocument work without .AddNewtonsoftJson()

As of .NET 5, it seems that JsonPatchDocument depends on the JsonSerializers from NewtonsoftJson. That is to say, in the source code for JsonPatchDocument, there is

[JsonConverter(typeof(JsonPatchDocumentConverter))]
public class JsonPatchDocument : IJsonPatchDocument
{

where the JsonConverter attribute is from the Newtonsoft Json Namespace.

I think I am able to define a JsonConverter so that it works in System.Text.Json. Something like this

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Newtonsoft.Json.Serialization;

namespace MyProject
{
    public class JsonPatchDocumentConverter : JsonConverter<JsonPatchDocument>
    {
        public override JsonPatchDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var result = JsonSerializer.Deserialize<List<Operation>>(ref reader, options);
            return new JsonPatchDocument(result, new DefaultContractResolver());
        }

        public override void Write(Utf8JsonWriter writer, JsonPatchDocument value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }
}

And then in Startup.cs,

        services.AddControllersWithViews().AddJsonOptions(options =>
        {
            options.JsonSerializerOptions.Converters.Add(new JsonPatchDocumentConverter());
        });

... OR alternatively, inherit JsonPatchDocument and define the attribute there

namespace MyProject        
{
    [System.Text.Json.Serialization.JsonConverter(typeof(JsonPatchDocumentConverter))]
    public class JsonPatchDocument : Microsoft.AspNetCore.JsonPatch.JsonPatchDocument
    {

    }
}

and then use the JsonPatchDocument subclass so that the JsonConverter from the attribute is picked up by System.Text.Json

public async Task<IActionResult> Patch(Guid id, 
                            [FromBody] MyProject.JsonPatchDocument patchDocument)
{
Related