aspnetcore
aspnetcore copied to clipboard
JsonPatchDocument: empty json of patch operation causes exception in ApplyTo
Describe the bug
When sending an empty json-patch operation (e.g. [ { } ]) the request is not rejected due to a malformed json but it get's deserialized and throws an exception in the JsonPatchDocument.ApplyTo() method which is neither expected not documented.
To Reproduce
Create an api-controller action:
public ActionResult<Foo> Patch(JsonPatchDocument<Foo> patchDoc) {
var origFoo = new Foo();
patchDoc.ApplyTo(origFoo, ModelState); // this throws an unexpected exception
}
public class Foo {
public String Name { get; set; }
}
Now make a request to your action with the payload [ { } ].
Exceptions (if any)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request.
System.ArgumentNullException: Value cannot be null. (Parameter 'path')
at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Add(String path, Object value, Object objectToApplyTo, Operation operation)
at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Add(Operation operation, Object objectToApplyTo)
at Microsoft.AspNetCore.JsonPatch.Operations.Operation`1.Apply(TModel objectToApplyTo, IObjectAdapter adapter)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter, Action`1 logErrorAction)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, Action`1 logErrorAction)
at Microsoft.AspNetCore.Mvc.JsonPatchExtensions.ApplyTo[T](JsonPatchDocument`1 patchDoc, T objectToApplyTo, ModelStateDictionary modelState, String prefix)
at Microsoft.AspNetCore.Mvc.JsonPatchExtensions.ApplyTo[T](JsonPatchDocument`1 patchDoc, T objectToApplyTo, ModelStateDictionary modelState)
at MWE_JsonPatchApplyToException.Controllers.ExampleController.Patch(JsonPatchDocument`1 patchDocument) in C:\Github\MWE-EmptyJsonPatchOperationThrowsException\Controllers\ExampleController.cs:line 16
at lambda_method(Closure , Object , Object[] )
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Further technical details
- ASP.NET Core version 3.1
- Include the output of
dotnet --info.NET SDK (reflecting any global.json): Version: 5.0.100-preview.8.20417.9 Commit: fc62663a35
Runtime Environment: OS Name: Windows OS Version: 10.0.19041 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.100-preview.8.20417.9\
Host (useful for support): Version: 5.0.0-preview.8.20407.11 Commit: bf456654f9
.NET SDKs installed: 2.1.202 [C:\Program Files\dotnet\sdk] 2.1.505 [C:\Program Files\dotnet\sdk] 2.1.507 [C:\Program Files\dotnet\sdk] 2.1.508 [C:\Program Files\dotnet\sdk] 2.1.509 [C:\Program Files\dotnet\sdk] 2.1.511 [C:\Program Files\dotnet\sdk] 2.1.513 [C:\Program Files\dotnet\sdk] 2.1.514 [C:\Program Files\dotnet\sdk] 2.1.515 [C:\Program Files\dotnet\sdk] 2.1.516 [C:\Program Files\dotnet\sdk] 2.1.517 [C:\Program Files\dotnet\sdk] 2.1.602 [C:\Program Files\dotnet\sdk] 2.1.604 [C:\Program Files\dotnet\sdk] 2.1.700-preview-009618 [C:\Program Files\dotnet\sdk] 2.1.700 [C:\Program Files\dotnet\sdk] 2.1.701 [C:\Program Files\dotnet\sdk] 2.1.800-preview-009677 [C:\Program Files\dotnet\sdk] 2.1.800-preview-009696 [C:\Program Files\dotnet\sdk] 2.1.800 [C:\Program Files\dotnet\sdk] 2.1.801 [C:\Program Files\dotnet\sdk] 2.1.802 [C:\Program Files\dotnet\sdk] 2.2.106 [C:\Program Files\dotnet\sdk] 2.2.107 [C:\Program Files\dotnet\sdk] 2.2.110 [C:\Program Files\dotnet\sdk] 2.2.401 [C:\Program Files\dotnet\sdk] 3.0.100-preview7-012821 [C:\Program Files\dotnet\sdk] 3.0.100-preview9-014004 [C:\Program Files\dotnet\sdk] 3.0.100-rc1-014190 [C:\Program Files\dotnet\sdk] 3.0.100 [C:\Program Files\dotnet\sdk] 3.1.100-preview2-014569 [C:\Program Files\dotnet\sdk] 3.1.102 [C:\Program Files\dotnet\sdk] 3.1.201 [C:\Program Files\dotnet\sdk] 3.1.301 [C:\Program Files\dotnet\sdk] 3.1.302 [C:\Program Files\dotnet\sdk] 3.1.402 [C:\Program Files\dotnet\sdk] 5.0.100-preview.8.20417.9 [C:\Program Files\dotnet\sdk]
.NET runtimes installed: Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0-preview9.19424.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0-rc1.19457.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0-preview2.19528.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.0-preview.8.20414.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0-rc1-19456-20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0-preview2.19525.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.0-preview.8.20407.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.0.0-rc1-19456-20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.0-preview2.19525.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.0-preview.8.20411.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
- Microsoft Visual Studio Professional 2019, Version 16.7.3
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Thanks for contacting us. Can you also clarify why would you do PATH with no content?
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.
See our Issue Management Policies for more information.
Sorry for the late response. I wouldn't do that but we are discussing if we allow our customers to access the PATCH endpoint. And to avoid wrong input we need to validate the data and a PATCH request without a PATH is obviously not correct w.r.t the spec. Therefore it was surprising that the requests isn't rejected during model deserialization and instead throws an undocumented exception.
Hi,
I think I've come through the same issue in .NET 5. If it is indeed the same issue, then it is more global than just the PATCH request.
Here is my code (NRT enabled):
public class Foo
{
[Required]
[JsonProperty(Required = Required.Always)]
public string Value { get; set; } = null!;
}
[HttpPost]
public Foo Put([FromBody] Foo testModel)
{
return testModel;
}
When I pass {} as the request body, I get two different behaviors, depending whether I'm using Newtonsoft.Json or System.Text.Json:
Newtonsoft.Json
This code doesn't return a 400 error, but instead is called with a null parameter, which is not expected. Returns a null body with a 204 code
System.Text.Json
The code is called with an object, and returns:
{
"value": null
}
I think that several things are going wrong here:
- Newtonsoft.Json returns null instead of an object, while the parameter is not nullable.
- A null is passed to the non-nullable parameter. I'd expect this to throw, even though I know that ApiExplorer isn't aware of nullability just yet
- In the STJ case, the Required attribute is completely ignored in the validation and says the object is valid.
Related : #32375
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
My issue is more related to the issue #17927
I am working on a scim 2.0 implementation and thought it would be a great idea to use the JsonPatchDocument
A scim valid sample request would look like this (as described in RFC7644 - Section 3.5.2
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{
"op": "replace",
"value": {
"active": true
}
}]
}
So I made a wrapper class, that wraps the schemas attribute as a string[] and used the Microsoft.AspNetCore.JsonPatch.Operations.Operations<TModel> for parsing the operations. The request gets parsed successfully.
I then make a new JsonPatchDocument and apply it to my existing model
JsonPatchDocument<UserResource> patchDocument = new(patchDoc.Operations, new DefaultContractResolver());
patchDocument.ApplyTo(userResource);
This however leaves me with an exception:
System.ArgumentNullException: Value cannot be null. (Parameter 'path')
at System.ArgumentNullException.Throw(String paramName)
at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
at Microsoft.AspNetCore.Shared.ArgumentNullThrowHelper.ThrowIfNull(Object argument, String paramName)
at Microsoft.AspNetCore.JsonPatch.Internal.ParsedPath..ctor(String path)
at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Replace(Operation operation, Object objectToApplyTo)
at Microsoft.AspNetCore.JsonPatch.Operations.Operation`1.Apply(TModel objectToApplyTo, IObjectAdapter adapter)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo)
at myproject.SCIMController.PatchUser(Guid tenantId, Guid userId, PatchOperation`1 patchDoc) in C:\\dev\\myproject\\Controllers\\SCIMController.cs:line 270
Settting path to "" leaves me with this exception:
Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException: The target location specified by path segment '' was not found.
at Microsoft.AspNetCore.JsonPatch.Internal.ErrorReporter.<>c.<.cctor>b__1_0(JsonPatchError error)
at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Replace(Operation operation, Object objectToApplyTo)
at Microsoft.AspNetCore.JsonPatch.Operations.Operation`1.Apply(TModel objectToApplyTo, IObjectAdapter adapter)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter)
at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo)
at myproject.SCIMController.PatchUser(Guid tenantId, Guid userId, PatchOperation`1 patchDoc) in C:\\dev\\myproject\\Controllers\\SCIMController.cs:line 270
The user in #17927 states, that this works in a JSON8 library.
JSON8 apply demo (https://json8.github.io/patch/demos/apply/)
Input
{
"id": "486ad2d5-91b0-4a1c-ac46-3783f48d6b21",
"active": false,
"externalId": null,
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User"
],
"userName": "[email protected]",
"name": {
"familyName": "dummy",
"givenName": "user"
},
"emails": [
{
"primary": true,
"value": "[email protected]"
}
],
"meta": {
"created": "1970-01-01T00:00:00+00:00",
"lastModified": "1970-01-01T00:00:00+00:00",
"resourceType": "User",
"location": "/scim/v2/Users/486ad2d5-91b0-4a1c-ac46-3783f48d6b21"
}
}
Patch
[
{
"op": "replace",
"path": "",
"value": {
"active": true
}
}
]
Output
{
"active": true
}
This leaves me a bit confused, since the JSONPatch RFC 6902 - Section 4 states the following:
Additionally, operation objects MUST have exactly one "path" member. That member's value is a string containing a JSON-Pointer value [RFC6901] that references a location within the target document (the "target location") where the operation is performed.
Additionally, https://jsonpatch.me/ allows the same kind of patching (path="")
Hey looks like there hasn't been progress on this, but just wanted to add some more info:
- RFC 6901 - Section 5 specifically calls this out in one of their examples for JSON Pointers.
- Microsoft's documentation on
JsonPatchDocumentrefers directly to the JSON Pointer documention in reference to thepathproperty on an Operation: https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-10.0#path-syntax - Microsoft Co-Pilot will recommend using
path: "/"orpath: ""when prompted asking how to PATCH an enitre model/object using .NET/C#.
Obviously, the workaround is to break up all the individual root properties/fields into separate Operations, but that adds a lot of overhead for scenarios where partial updates are not required. The other option would be adding a PUT operation to the same controller, but again adds unnecessary overhead. Hope to see this supported on JsonPatchDocument