3

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?

Joshit
  • 1,238
  • 16
  • 38
  • Did you migrate 2.2 to 3.0 as per the [official documentation](https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio) ? I tested it and it worked well . Could you share a demo that can reproduce the issue ? – Xueli Chen Oct 25 '19 at 09:14
  • I didn´t get the right package. It was not clear to me, that I had install a new package, beside the call of AddNewtonsoftJson(). in startup. But thank you anyway for looking at this! – Joshit Oct 25 '19 at 11:27

2 Answers2

9

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://learn.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()

  • Thank you very much! Installing the package solved my issue – Joshit Oct 25 '19 at 11:28
  • There won´t be a possibility to get rid of newtonsoft, because JsonPatchDocument relies on newtonsoft internally. Maybe they replace this package as well in future releases. – Joshit Oct 25 '19 at 11:30
  • Thank you this solved my problem with [FromBody] in a post action setting the model to null.After installing the package it works perfectly. – TResponse Nov 28 '19 at 20:24
2

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)
{
KTCheek
  • 309
  • 2
  • 4