GithubHelp home page GithubHelp logo

aspnet / jsonpatch Goto Github PK

View Code? Open in Web Editor NEW
103.0 42.0 47.0 453 KB

[Archived] JSON PATCH library. Project moved to https://github.com/aspnet/AspNetCore

License: Apache License 2.0

Batchfile 0.17% Shell 3.07% C# 94.56% PowerShell 2.20%
aspnet-product

jsonpatch's Introduction

JsonPatch [Archived]

This GitHub project has been archived. Ongoing development on this project can be found in https://github.com/aspnet/AspNetCore.

This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the AspNetCore repo.

jsonpatch's People

Contributors

ajaybhargavb avatar arerlend avatar aspnetci avatar brennanconroy avatar davidfowl avatar dougbu avatar drovani avatar eilon avatar iamcerba avatar javiercn avatar jbagga avatar jkotalik avatar juntaoluo avatar kevindockx avatar kichalla avatar mikeharder avatar natemcmaster avatar ntaylormullen avatar pakrym avatar pranavkm avatar ryanbrandenburg avatar rynowak avatar sandorfr avatar tagc avatar tfleury avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonpatch's Issues

Performing Test Operation with Operation<T> on a JsonPatchDocument<T> does not throw

The implementation of Operation differs from Operation in that Operation throws a JsonPatchException when Test is used. This type of exception is caught and "reported", then the ApplyTo is short circuited but the method returns. In this scenario the caller has no way of knowing that anything failed.
Recommend changing the exception type thrown by Operation.ApplyTo to be consistent with Operation. Alternatively (or in addition to) the catch in JsonPatchDocument should rethrow the exception that it caught so the caller is always aware of any exception that occurred.

Honor JsonProperty when serializing a JsonPatchDocument.

An issue was logged on the issue list of Marvin.JsonPatch (on which AspNetCore.JsonPatch is based, which is why I'm logging this issue) regarding honoring JSON.NET's JsonProperty (KevinDockx/JsonPatch#44 (comment)).

The AspNetCore version supports this when applying a patchDoc to a class that has properties decorated with [JsonProperty] attributes. Marvin.JsonPatch doesn't (PCL stuff), so that's not an issue for AspNetCore's version. But it's the last comment of Amnesia1187 that's of importance, as it affects AspNetCore.JsonPatch.

It's regarding creating a JsonPatchDocument on the consumer-side of things. Such a document is serialized to a Json list of operations, and the "path"-portion contains the property name the patch operation affects. That part apparently does not respect JsonProperty values.

So, if you create a JsonPatchDocument, and MyClass contains a property "Name" decorated with JsonProperty("RealName"), it will be serialized to path: "/name" instead of "realName".

I feel it should serialize to realName. Thoughts on this?

Patch an aggregated DTO

A somewhat contrived, but nonetheless important, example.

Assume the following case whereby UserDetails is an aggregate DTO (not sure of correct terminology, but basically a model of collected information from different stores/services) which is used by a RESTful web service. It does not necessarily have the same property names as the objects it collects together.

public class UserDetails
{
    public int UserId { get;set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
    public int? UserGroupId { get;set; } // FK in a different database
}

Let our stores persist the following models:

public class User
{
    public int Id { get; set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
}
public class UserGroup
{
    public int UserId { get; set; }
    public int GroupId { get; set; }
}

Let the UserDetails object be populated thusly:

User user = _userService.GetUser(userId) ?? throw new Exception();
UserGroup userGroup = _userGroupService.GetUserGroup(user.Id);

UserDetails userDetails = new UserDetails {
    UserId = user.Id,
    GivenName = user.GivenName,
    Surname = user.Surname,
    UserGroupId = userGroup?.GroupId
};

That is, setting FirstName or Surname should delegate to the UserService, and UserGroupId to the GroupService.

This UserDetails object is used for GET and PUT, the logic here is pretty trivial, however a JSONPatch document for this object is sent for PATCH requests. This is apparently much more complicated.

How can we go about changing the user's group? The best ('best' being used very loosely) I came up with is this:

JsonPatchDocument<UserDetails> patch;

// This likely works fine, because the properties in `UserDetails`
// are named the same as those in `User`
List<string> userProps = new List<string> {"/givenName", "/surname"};
if (patch.Operations.Any(x => userProps.Contains(x.path))) {
    User user = _userService.GetUserByUserId(userId);
    patch.ApplyTo(user);
    _userService.SetUser(userId, user);
}

// Do specialised stuff for UserGroup
// Can't do ApplyTo() because `UserDetails.UserGroupId` is not named the same as `UserGroup.GroupId`
IEnumerable<Operation<UserDetails>> groupOps = patch.Operations.Where(x => x.path == "/userGroupId");
foreach (Operation<UserDetails> op in groupOps)
{
    switch (op.OperationType)
    {
        case OperationType.Add:
        case OperationType.Replace:
            _groupService.SetOrUpdateUserGroup(userId, (int?)(op.value));
            break;

        case OperationType.Remove:
            _groupService.RemoveUserGroup(userId);
            break;
    }
}

Which is pretty garishly awful. It's a lot of boilerplate, and relies on a magic string.

Something like patch.Path(x => x.UserGroupId ) which returns "/userGroupId" would at least get rid of the magic strings.

JsonPatch seems pretty limited in this regard, seems more tailored towards systems where there is a 1:1 mapping between DAO (entities) and DTO (model). Anyone got any good ideas? Can't be hard to beat the tripe I came up with!!

Downgrade warnings during restore

JsonPatch\src\Microsoft.AspNetCore.JsonPatch\project.json(36,53): warning NU1012: Dependency conflict. Microsoft.CSharp 4.3.0-preview1-24530-03 expected System.Reflection.TypeExtensions >= 4.3.0-preview1-24530-03 but received 4.1.0
JsonPatch\src\Microsoft.AspNetCore.JsonPatch\project.json(36,53): warning NU1012: Dependency conflict. System.Dynamic.Runtime 4.3.0-preview1-24530-03 expected System.Reflection.TypeExtensions >= 4.3.0-preview1-24530-03 but received 4.1.0

Extension point

As far as I can see the extension point is the ObjectAdapter. So, basically the idea is to create a wrapper around it which implements the same interface. But ObjectAdapter is not injectable because of the Action<JsonPatchError> constructor parameter.

In addition, I saw that here and here it is explicitly set to null.

Does it make sense to make it optional in order to allow it to be injectable?

That's what I did when I used this version https://github.com/KevinDockx/JsonPatch, but the parameterless constructor is not available anymore.

public interface IPatchAdapter : IObjectAdapter
{
}

public class PatchAdapter : IPatchAdapter
{
    private readonly IObjectAdapter _objectAdapter;

    public PatchAdapter(IObjectAdapter objectAdapter)
    {
        _objectAdapter = Check.NotNull(objectAdapter, nameof(objectAdapter));
    }

    public void Add(Operation operation, object objectToApplyTo)
    {
        // ...
    }

    public void Copy(Operation operation, object objectToApplyTo)
    {
        // ...
    }

    public void Move(Operation operation, object objectToApplyTo)
    {
        // ...
    }

    public void Remove(Operation operation, object objectToApplyTo)
    {
        // ...
    }

    public void Replace(Operation operation, object objectToApplyTo)
    {
        // ...
    }
}

Incorrect move operation behavior

When performing a move operation, target object is not moved, but it is removed from source and a copy is added to the destination.
There is no problem with value types, but there is one with reference types : reference was lost.

To handle move operation, ObjectAdapter gets the moved property, removes it from the source, then adds it to the destination. That's OK.

The issue is in ListAdapter, PocoAdapter and ExpandoAdapter (don't know why, but not in DictionaryAdapter), in TryConvertValue method where the value is copied regardless of its type.

If object's type is compatible with target property's type, the reference should be kept.

Check if round tripping an operation's value is required

We currently round trip the 'value' provided in an 'operation' here:
https://github.com/aspnet/JsonPatch/blob/dev/src/Microsoft.AspNetCore.JsonPatch/Internal/ConversionResultProvider.cs#L15

Valid types in JSON:
number,
string,
bool,
array,
object,
null

Some things we need to verify in the above line of code:

Some scenarios to consider if we plan to avoid round tripping:
value type: string
target type: System.Guid

value type: Int64 (default json.net type for integers)
target type: Int32

@Eilon

Patching of dictionaries should be case sensitive on keys

The current code handles all (dynamic(example: ExpandoObject implements IDictionary<string,object>) or not) dictionaries which implement IDictionary<string, object> in a case Insensitive way. This is an incorrect behavior.

For example:

  1. When Json.net deserializes data into a regular IDictionary<,> it considers the keys case-sensitive.
  2. When Json.net deserializes data into a regular Poco type, it considers the property names case-Insensitive.

Considering the above behavior, we should do the following in json patch:

  1. For dynamic types (like ExpandoObject, DynamicObject etc.), the matching of keys should be case-Insensitive. This is to keep the behavior consistent with a Poco type where the property names are considered case-Insensitive.
  2. For dictionaries, keep the default json.net behavior, i.e. keep them case sensitive.

@dougbu @rynowak

Why has dynamic patching support apparently been dropped between 1.0.0 and 1.1.0?

I've been developing code that relies on dynamic JSON patching. In my ".NET Core library" project everything works as expected, and all tests pass. However, when I package up this project and use it as a dependency within an ASP.NET Core web application (.NETCore App 1.1.0), the exact same operations result in errors like this:

{Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException: The target location specified by path segment 'foo' was not found.
   at Microsoft.AspNetCore.JsonPatch.Internal.ErrorReporter.<>c.<.cctor>b__1_0(JsonPatchError error)
   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.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter)
   at <<Company>>.<<Product>>Web.Utility.JsonRequestProcessor`1.TryParseModifications(String request, T& discoveredModifications, T& discoveredFixedDetails, Exception& requestException)}	System.Exception {Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException}

It had me scratching my head for a while but I believe I've tracked the root of this issue down to the fact that my library uses Microsoft.AspNetCore.JsonPatch v1.0.0 whereas my web application would be using Microsoft.AspNetCore.JsonPatch v1.1.0.

Homing in on Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Add(String path, Object value, Object objectToApplyTo, Operation operation) I compared the differences between the two versions.

From v1.0.0, it looks like it's handling dynamic paths:

private void Add(
    string path,
    object value,
    object objectToApplyTo,
    Operation operationToReport)
{
    if (path == null)
    {
        throw new ArgumentNullException(nameof(path));
    }

    if (objectToApplyTo == null)
    {
        throw new ArgumentNullException(nameof(objectToApplyTo));
    }

    if (operationToReport == null)
    {
        throw new ArgumentNullException(nameof(operationToReport));
    }

    // ...

    if (treeAnalysisResult.UseDynamicLogic)
    {
        // ...
    }
    // ...
}

However, from v1.1.0 this method seems to omit all dynamic path support:

private void Add(
    string path,
    object value,
    object objectToApplyTo,
    Operation operation)
{
    if (path == null)
    {
        throw new ArgumentNullException(nameof(path));
    }

    if (objectToApplyTo == null)
    {
        throw new ArgumentNullException(nameof(objectToApplyTo));
    }

    if (operation == null)
    {
        throw new ArgumentNullException(nameof(operation));
    }

    var parsedPath = new ParsedPath(path);
    var visitor = new ObjectVisitor(parsedPath, ContractResolver);

    IAdapter adapter;
    var target = objectToApplyTo;
    string errorMessage;
    if (!visitor.TryVisit(ref target, out adapter, out errorMessage))
    {
        var error = CreatePathNotFoundError(objectToApplyTo, path, operation, errorMessage);
        ErrorReporter(error);
        return;
    }

    if (!adapter.TryAdd(target, parsedPath.LastSegment, ContractResolver, value, out errorMessage))
    {
        var error = CreateOperationFailedError(objectToApplyTo, path, operation, errorMessage);
        ErrorReporter(error);
        return;
    }
}

Why has this been done?

Failed to apply patch on json as string

I'm trying to apply patch on the JSON feeds as string nad getting error
Unable to cast object of type 'Newtonsoft.Json.Serialization.JsonPrimitiveContract' to type 'Newtonsoft.Json.Serialization.JsonObjectContract'.
Code sample.

var patch = new JsonPatchDocument();
patch.Replace("/region", "test");
patch.ApplyTo("{"region": "amer", "foo1": {"foo11": "val1", "foo12": 2}}");

I tried using latest version "Microsoft.AspNetCore.JsonPatch" it's failing with the different error. "The target location specified by path segment 'region' was not found."

Multiple actions matched

Is it possible to have multiple controller actions which has a different signature?

[HttpPatch("items/{id}")]
public IActionResult PatchAsync(string id, [FromBody]JsonPatchDocument<Document1> document1)
{
}

[HttpPatch("items/{id}")]
public IActionResult PatchAsync(string id, [FromBody]JsonPatchDocument<Document2> document2)
{
}

I am getting the "Microsoft.AspNetCore.Mvc.Internal.AmbiguousActionException: Multiple actions matched." exception. Should I use a custom constraint in such case or JsonPatch has something built-in for such scenario?

Cannot get this to work in ASP.NET Core MVC

Controller:

        [HttpPatch]
        public IActionResult Patch(JsonPatchDocument<Contact> patch)
        {
            patch.ApplyTo(contact, ModelState);

            if (!ModelState.IsValid)
            {
                return new BadRequestObjectResult(ModelState);
            }

            return Ok(contact);
        }

When I make the following request:

PATCH /api/contacts HTTP/1.1
Host: localhost:51834
Content-Type: application/json-patch+json
Cache-Control: no-cache

[{ "op": "add", "path": "/firstname", "value": "John" }] 

No operations are bound to the patch parameter and therefore no changes are made.

Honour Validation attributes

Currently only type checking is performed. It would be good if the object adaptor also honoured validation attributes on the object for example:

        [Range(18, 65)]
        public int Age { get; set; }

Currently I could set the age property to 10 using a patch operation.

Support for strongly typed dictionarys

I have the following model (simplified for example)

public class FieldResponseModel
{
        public int Id { get; set; }
  	public string Name { get; set; }
        public string Value { get; set; }
        public IDictionary<string, TranslationModel> Translations { get; set; }
}
public class TranslationModel
{
    public string Language {get;set;}
    public string Value {get;set;}
}

I want to perform the following add operation to add a new translation to the Dictionary
[{ "op":"add", "path":"/fieldResponseModel/tranlsations/es-ES", "value":{ "language":"es-ES", "Value":"some translated value" } }]

However, when calling patch.ApplyTo() I get the following exception:

The value "{\r\n "language": "es-ES",\r\n "Value": "some translated value",\r\n }" is not of type "TranslationModel" and cannot be used in this generic collection.\r\nParameter name: value.

Am I missing something my operation or is this type of add operation not supported?

Consider update version of Newtonsoft.Json

This project uses "Newtonsoft.Json": "6.0.6" therefore we have conflicts with other our assemblies because they use latest version 7.0.1. Old version of project used 7.0.1 version too (at least beta5)

Incompatible with netstandard1.5?

I'm trying to upgrade one of my libraries from ASP.NET 5 RC1 to ASP.NET Core RC2. I'm seeing the following error:

      Package Newtonsoft.Json 8.0.3 is not compatible with netstandard1.5 (.NETStandard,Version=v1.5). Package Newtonsoft.
  Json 8.0.3 supports:
        - net20 (.NETFramework,Version=v2.0)
        - net35 (.NETFramework,Version=v3.5)
        - net40 (.NETFramework,Version=v4.0)
        - net45 (.NETFramework,Version=v4.5)
        - portable-dnxcore50+net45+win8+wp8+wpa81 (.NETPortable,Version=v0.0,Profile=net45+wp80+win8+wpa81+dnxcore50)
        - portable-net40+sl5+win8+wp8+wpa81 (.NETPortable,Version=v0.0,Profile=Profile328)

The Newtonsoft.Json dependency is coming from Microsoft.AspNetCore.JsonPatch, as a dependency of Microsoft.AspNetCore.Mvc.Formatters.Json.

JsonPatch - ModelState is invalid

Hi,
I have a problem with jsonpatch in an asp.net core webapi project.
One of the controller:

    [HttpPatch("{id}")]
    public IActionResult Patch(int id, [FromBody]JsonPatchDocument<ContactPerson> contactPersonPatchDocument)
    {
        if (contactPersonPatchDocument == null)
        {
              return BadRequest();
        }

        var existingContactPerson = _context.ContactPersons.SingleOrDefault(x => x.Id == id);
        if (existingContactPerson == null)
        {
            return NotFound();
        }

        contactPersonPatchDocument.ApplyTo(existingContactPerson, ModelState);
        _context.SaveChanges();
        return Ok();
    }

`
I have the id because I want to update an specific entity. Is this right?

I'm using swagger for the http request. Here's my patch request:
image

When I run this request I get the following response in swagger:
image

And Visual Studio writes this output:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 PATCH http://localhost:5000/api/ContactPersons/1 application/json 75
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method WebAPI.Controllers.ContactPersonsController.Patch (WebAPI) with arguments (1, ) - ModelState is Invalid
Microsoft.AspNetCore.Mvc.StatusCodeResult:Information: Executing HttpStatusCodeResult, setting HTTP status code 400
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action WebAPI.Controllers.ContactPersonsController.Patch (WebAPI) in 9.9579ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 26.4883ms 400

For information:

  • The other operations like post, get,... are working well.
  • I don't use DTOs, are they necessary?

What I'm doing wrong? Thanks.

[JsonPatch] Add test operation support

From @kirthik on March 20, 2015 9:37

Test operation in code today does not compare anything. Removing the TODO from code. Pasting code snippet for reference.

var conversionResultTuple = PropertyHelpers.ConvertToActualType(
                typeOfFinalPropertyAtPathLocation,
                operation.value);

            // conversion successful
            if (conversionResultTuple.CanBeConverted)
            {
                // COMPARE - TODO
            }
            else
            {
                throw new JsonPatchException<T>(operation,
                    string.Format("Patch failed: provided value is invalid for property type at location path: {0}",
                        operation.path),
                    objectToApplyTo);
            }

Copied from original issue: aspnet/Mvc#2218

Patching fails for non 'object' value type dictionaries

Given a model like this:

public class DemoModel
{
  public DemoModel()
  {
    this.Numbers = new Dictionary<string, int>();
  }

  public IDictionary<string, int> Numbers { get; private set; }
}

When JSON serialized, the object looks like this:

{
  "Numbers": {
    "one":1,
    "two":2
  }
}

If I try to create a patch document and apply it, it fails:

[Fact]
public void DictionaryTest()
{
  var model = new DemoModel();
  model.Numbers["one"] = 1;
  model.Numbers["two"] = 2;

  var patch = new JsonPatchDocument();
  patch.Operations.Add(new Operation("add", "/Numbers/three", null, 3));
  patch.ApplyTo(model);

  Assert.Equal(3, model.Numbers.Count);
  Assert.Equal(1, model.Numbers["one"]);
  Assert.Equal(2, model.Numbers["two"]);
  Assert.Equal(3, model.Numbers["three"]);
}

The test fails on the patch.ApplyTo(model) line:

at Microsoft.AspNetCore.JsonPatch.Helpers.ObjectTreeAnalysisResult..ctor(Object objectToSearch, String propertyPath, IContractResolver contractResolver)
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Add(String path, Object value, Object objectToApplyTo, Operation operationToReport)
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Add(Operation operation, Object objectToApplyTo)
   at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument.ApplyTo(Object objectToApplyTo, IObjectAdapter adapter)
   at DemoProject.DictionaryTest() in C:\path\to\Fixture.cs:line 73
Result Message: Unable to cast object of type 'Newtonsoft.Json.Serialization.JsonDictionaryContract' to type 'Newtonsoft.Json.Serialization.JsonObjectContract'.

Is this a known issue? Or is there a workaround?

Add builtin JObjectAdapter that implements IAdapter

As for now builtin implementations for IAdapter are

DictionaryAdapter
ExpandoObjectAdapter
ListAdapter
PocoAdapter

In my case is useful to have JsonPatchDocument<JObject> and apply it to JObject from Newtonsoft.Json.

I use workaround

var expandoObject = new ExpandoObject();
patchDoc.ApplyTo(expandoObject);
var jObject = JObject.FromObject(expandoObject);

However, it would be nice to have builtin IAdapter for JObject so I can call

var jObject = new JObject();
patchDoc.ApplyTo(jObject)

without any errors.

Adding/Removing from a collection by an index value

I need to be able to add/remove object from a subcollection of my item - imagine the following json patch string
{"op":"remove", "path":"/a/b/123"}]
or
{"op":"add","path":"/a/b", "value":"{"a":"b"}"

where 123 is my database Index, and not a list position (which my client has no actual way of knowing about)

As of now, I see no way of actually implementing this (maybe I missed something, but I went through the code of both ListAdapter, and DictionaryAdapter).

If I use a Dictionary as the model, the remove operation will work, but the add will not (I could have the client send fake ids, but that would be really odd)

If I use a List as the model, well I won't be able to remove by a database Index (I could order the values in the exact same way the client does, but again - this is odd).

Implementing IObjectAdapter myself isn't a good option either - besides the fact that it isn't generic, and won't allow me to use multiple IObjectAdapters via DI - it takes a lot of code to actually implement properly, and will basically be copying your code with minor changes.

I don't expect it to be able to actually search a list by an Id property, but I do think this entire package should be more configurable to actually allow me to define how to search a list with a given index, without copy pasting your code and applying basic changes.

Any plan for supporting the 'test' operation?

NOT SUPPORTED NOW
I found that the test operation is not supported till now. ( JsonPatch, Version=1.1.0.0 )

A part of standard

Refer to rfc6902 section-4.6 from the IETF, test is one of the operations that is specified.

It's useful

test is useful in some scenario. Taking HTTP patch as example, it can be used for checking consistency when going with HTTP PATCH method.

If test operation is supported, we can simply include a consistency stamp as the first operation. if test failed no patch(update) action will take effect.
This can make the patch being more atomic, otherwise we have to implement consistency checking separately( such as an ugly request query parameter.)

// http request payload
[
{op:'test', path:'/consistencyStamp', value:'some value for stamp' },
{op:'replace', path:'/enabled',value:'true'}
// ...  //other operations
]

Thank you, guys

Since JsonPatch is now token as the first-class support in asp.net core, and test operation it's useful, It should be implemented integrally with the standard.
Microsoft is great while making .net core open-source, and all of guys like you is cool to do these jobs.
come on, It can be more perfect. and many thanks to you man.

Property names in Path should use the supplied ContractResolver instead of making it lower case

Given that property names in javascript/json are case sensitive, the serialization of operations to json should honor the supplied ContractResolver instead of always returning in lower case

https://github.com/aspnet/JsonPatch/blob/dev/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs#L718

718         private string GetPath<TProp>(Expression<Func<TModel, TProp>> expr) 
719         { 
720             return "/" + GetPath(expr.Body, true).ToLowerInvariant(); 
721         } 

https://github.com/aspnet/JsonPatch/blob/dev/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocumentOfT.cs#L776
private string GetPropertyNameFromMemberExpression(MemberExpression memberExpression)
uses the contract resolver correctly, but the GetPath function above forces it to lower case.

Neither JSON patch: https://tools.ietf.org/html/rfc6902
nor JSON Pointer: https://tools.ietf.org/html/rfc6901
require it to be lower case, so letting developers control the case through would be a good idea

class property setters with `null` checks makes Typed JsonPatchDocument tests fail.

class property setters with null checks like the following makes tests fail.
Eg. I added a backing field for SimpleDTO.StringProperty with a null check like the following.
These tests appear in NestedObjectsTests and ObjectAdapterTests class.

    public class SimpleDTO
    {
        public List<int> IntegerList { get; set; }
        public IList<int> IntegerIList { get; set; }
        public int IntegerValue { get; set; }
        string stringProperty;

        public string StringProperty
        {
            get
            {
                return stringProperty;
            }

            set
            {
                if (value == null) {
                    throw new ArgumentNullException();
                }
                stringProperty = value;
            }
        }

Test Name:  Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Replace
Test FullName:  Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Replace
Test Source:    D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\NestedObjectTests.cs : line 886
Test Outcome:   Failed
Test Duration:  0:00:00.036

Result StackTrace:  
at Newtonsoft.Json.Serialization.ExpressionValueProvider.SetValue(Object target, Object value)
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Remove(String path, Object objectToApplyTo, Operation operationToReport) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Adapters\ObjectAdapter.cs:line 687
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Replace(Operation operation, Object objectToApplyTo) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Adapters\ObjectAdapter.cs:line 725
   at Microsoft.AspNetCore.JsonPatch.Operations.Operation`1.Apply(TModel objectToApplyTo, IObjectAdapter adapter) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Operations\OperationOfT.cs:line 67
   at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\JsonPatchDocumentOfT.cs:line 674
   at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\JsonPatchDocumentOfT.cs:line 636
   at Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Replace() in D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\NestedObjectTests.cs:line 903
   at Microsoft.AspNetCore.JsonPatch.Test.SimpleDTO.set_StringProperty(String value) in D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\SimpleDTO.cs:line 28
   at Newtonsoft.Json.Serialization.ExpressionValueProvider.SetValue(Object target, Object value)
Result Message: 
Error setting value to 'StringProperty' on 'Microsoft.AspNetCore.JsonPatch.Test.SimpleDTO'.
Value cannot be null.

-----------------------
Test Name:  Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Move
Test FullName:  Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Move
Test Source:    D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\NestedObjectTests.cs : line 1683
Test Outcome:   Failed
Test Duration:  0:00:00.005

Result StackTrace:  
at Newtonsoft.Json.Serialization.ExpressionValueProvider.SetValue(Object target, Object value)
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Remove(String path, Object objectToApplyTo, Operation operationToReport) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Adapters\ObjectAdapter.cs:line 687
   at Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter.Move(Operation operation, Object objectToApplyTo) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Adapters\ObjectAdapter.cs:line 411
   at Microsoft.AspNetCore.JsonPatch.Operations.Operation`1.Apply(TModel objectToApplyTo, IObjectAdapter adapter) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\Operations\OperationOfT.cs:line 70
   at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\JsonPatchDocumentOfT.cs:line 674
   at Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1.ApplyTo(TModel objectToApplyTo) in D:\0\JsonPatch\src\Microsoft.AspNetCore.JsonPatch\JsonPatchDocumentOfT.cs:line 636
   at Microsoft.AspNetCore.JsonPatch.Test.NestedObjectTests.Move() in D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\NestedObjectTests.cs:line 1699
   at Microsoft.AspNetCore.JsonPatch.Test.SimpleDTO.set_StringProperty(String value) in D:\0\JsonPatch\test\Microsoft.AspNetCore.JsonPatch.Test\SimpleDTO.cs:line 28
   at Newtonsoft.Json.Serialization.ExpressionValueProvider.SetValue(Object target, Object value)
Result Message: 
Error setting value to 'StringProperty' on 'Microsoft.AspNetCore.JsonPatch.Test.SimpleDTO'.
Value cannot be null.

Short circuit on the first operation failure when multiple operations are part of the json patch request

This is a regression that I introduced with my check-in. I found this with a test in Entropy (I didn't knew there were tests for it in Entropy actually).

Once this fixed, revert the following commit in Entropy:
aspnet/Entropy@292a5c0

@rynowak @Eilon

For reference: Snippet from Json Patch spec:

Evaluation of a JSON Patch document begins against a target JSON
   document.  Operations are applied sequentially in the order they
   appear in the array.  Each operation in the sequence is applied to
   the target document; the resulting document becomes the target of the
   next operation.  Evaluation continues until all operations are
   successfully applied or until an error condition is encountered.

Reorganize tests and remove redundant integration tests

The tests are organized in a confusing structure and not as we usually organize them. There are a lot of redundant integration tests and some adapters lack unit tests.

1. This test suite can be used to ensure we are spec compliant. It can either be used to generate test code or parsed and tested.

  • 2. This folder has lots of integration tests for all operations (we should remove redundancy)
  • 3. Add integration tests for "Test" operation
  • 4. Clean up test object model classes - simplify by adding them to test classes that use them
  • 5. Ensure all test classes follow the Arrange, Act, Assert pattern; variable names are coherent/sensible (replace patchDoc with patchDocument, obj with object/targetObject etc.), remove unnecessary serialization and formatting of error messages
  • 6. ExpandoObjectAdapter and PocoAdapter do not have unit tests

This PR adds a new adapter called DynamicObjectAdapter with unit tests and integration tests in a folder called IntegrationTests.

cc @rynowak

Update Microsoft.AspNet.JsonPatch nuget package version

Hi everyone,

The ASP NET Core version of this package is at version 1.1.1: https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch/

But the ASP NET standard version of this package was not updated since 2015, at version 1.0.0-beta8: https://www.nuget.org/packages/Microsoft.AspNet.JsonPatch/1.0.0-beta8

I realise this may not be the proper platform to request this, but can you push to latest stable version for Microsoft.AspNet.JsonPatch on NuGet to 1.1.1 as well?

Simple add on Lists failing in 1.1.0+

Working on 1.0.0, but failing on 1.1.0 and 1.2.0-preview1-22759

Here I'm attempting to add a new entry on List named Mylist which already has two entries (at indexes zero and one)

[{"op":"add","path":"/mylist/2","value":{"myname":"foo","age":33}}]

Response:
JsonPatchException: The index value provided by path segment '2' is out of bounds of the array size.

I'm guessing it's incorrectly looking at the length of the list prior to adding. With a dash at the end instead of the new index, this will work:

[{"op":"add","path":"/mylist/-","value":{"myname":"foo","age":33}}]

AspNetCore.Mvc.Formatters.Json 1.1.1 should reference JsonPatch 1.1.1

Hi, I opened #48 which has resulted in a fix. Great! Thanks.

However, on updating AspNetCore.Mvc to 1.1.1, the reference chain doesn't reference this update to JsonPatch. Specifically AspNetCore.Formatters.Json still references the 1.1.0 version.

Maybe I'm misunderstanding how it should work. I can't (yet?) reference JsonPatch 1.1.1 separately.

JsonPatchDocument.Replace() yields invalid path when [JsonProperty] is used (1.1.0)

Title

In 1.1.0, JsonPatchDocument<T>.Replace() creates Operation<T> with invalid path, when [JsonProperty] from Newtonsoft.Json is used.

Functional impact

This is regression from "Microsoft.AspNetCore.JsonPatch": "1.0.0" and breaks existing behavior.

Minimal repro steps

global.json:

  "sdk": {
    "version": "1.0.0-preview2-1-003177"
  }

project.json:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.AspNetCore.JsonPatch": "1.1.0",
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.1.0"
    },
    "Newtonsoft.Json": "9.0.1"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

Program.cs:

using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.JsonPatch;
using Newtonsoft.Json;

namespace JsonPatchReplaceIssue
{
    public class Model
    {
        [JsonProperty]
        public string[] ArrayOfStringsOrNull { get; set; }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var patch = new JsonPatchDocument<Model>()
                .Replace(m => m.ArrayOfStringsOrNull, new[]
                {
                    "yo"
                });

            var operation = patch.Operations.Single();

            Debug.Assert(operation.path == $"/{nameof(Model.ArrayOfStringsOrNull).ToLowerInvariant()}",
                $"No it ain't! It is '{operation.path}'");
        }
    }
}

Expected result

The Operation<T>.path should be derived from the property in the expression. Expected path to be "/arrayofstringsornull" in this case.

Actual result

Actual path is "/", which is incorrect.

Further technical details

Regression from "Microsoft.AspNetCore.JsonPatch": "1.0.0" with .NET Core 1.0.
I have tested:

  • Change the JsonPatch dependency in project.json to 1.0.0 and it works as expected.
  • Remove [JsonProperty] and it works as expected.

Question

Am I doing it wrong? I'm assuming that Newtonsoft.Json's attributes are supported.

Reduce allocations for a typical patch scenario

For the following typical replace operation

[
    { "op": "replace", "path": "/Name", "value": "Roger Federer" }
]

Before:
image

After:
After making the following couple of changes, I see a 30% reduction in memory usage

  • Fix the round-tripping issue mentioned here #69
  • Avoid validating the JsonPatchDocument itself (AFAIK this is not required as an end user is not going to validate it but should be validating his model....that said validating the model while patching is an open question currently as we do not validate the model)

image

Honoring Microsoft.AspNet.Mvc.BindAttribute

Can this component honor the BindAttribute ?

i.e. If the class type that is being patched has an MVC BindAttribute and does not include certain property(ex: Prop1), then if Prop1 was found in the operations passed from the client, it will just be ignored.

Move forward in a nested list fails

Assume you have a nestable class like this one :

public class Nestable
    {
        public string Label { get; set; }

        public List<Nestable> Children { get; set; }

        public Nestable()
        {
            this.Children = new List<Nestable>();
        }
    }

Your have this object :

{
    Label: "object 1"
    Children: [
        { Label: "object 1.1", Children: [] },
        { Label: "object 1.2", Children: [] }
    ]
}

And you want to move 1.1 into 1.2. The operation should be :

[{
    op: "move",
    from: "/Children/0",
    path: "/Children/1/Children/-"
}]

And you expect to obtain :

{
    Label: "object 1"
    Children: [
        { Label: "object 1.2", Children: [
            { Label: "object 1.1", Children: [] }
        ] }
    ]
}

Correct ?

But it fails with :
The index value provided by path segment '1' is out of bounds of the array size.

It's because move gets value, removes source and add at destination. but in this special case, destination has moved...

JsonPatchException: Invalid JsonPatch operation thrown on model binding

When the operation is invalid, an exception will be thrown on model binding.
{
"value": "someValue",
"path": "/validPath",
"op": "invalidOperationName"
}

Why is that? I would have thought that the patch document would become null, with an error message in the modelstate, just like when the json body is invalid?
I dont want to return a 500 error to the caller when the operation is invalid...

Is this the intended behavior? If so, is there a workaround?

// root path for List<> type

I am getting a path string to be generated from an expression to the root object that looks like this:
//
That is, x => x is turning into '//' when it should just be '/'.
This is just the case when the generic type of the document is a list.

When applying the apply function(s) it seems to be happy with the double slash, but I don't think this should be the case.

ICollection<T> not supported for collection operations

It seems like only IList<> is supported.

And since Entity Framework Core uses HashSet (which implements ICollection<> but not IList<>) this prevents us from using JsonPatch to update EF Core Entities with subcollections (navigation properties).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.