Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any chance to get better nested diff information? #36

Open
Cyber1000 opened this issue Feb 11, 2023 · 4 comments
Open

Any chance to get better nested diff information? #36

Cyber1000 opened this issue Feb 11, 2023 · 4 comments
Labels
documentation Improvements or additions to documentation question Further information is requested

Comments

@Cyber1000
Copy link

With the following code (excluded the usings for simplicity)

var node1 = JsonNode.Parse("{\"foo\":[ {\"bar1\": \"res1\"} ]}");
var node2 = JsonNode.Parse("{\"foo\":[ {\"bar1\": \"res2\"} ]}");

var x = JsonDiffPatcher.Diff(node1, node2, new JsonPatchDeltaFormatter());
Console.WriteLine(x);

I'm getting follwing:

[
  {
    "op": "remove",
    "path": "/foo/0"
  },
  {
    "op": "add",
    "path": "/foo/0",
    "value": {
      "bar1": "res2"
    }
  }
]

I would like to get something like:

[
  {
    "op": "replace",
    "path": "/foo/0/bar1"
    "value": "res2"
  }
]

I've looked into DefaultFormatter and it seems that JsonDiffDelta has already 2 change entries: one for remove and one for add.

What's the best method to check arrays recursively (the way I want)? Is there some kind of flag or would I need to override FormatArray somehow?

Thanks!

@Cyber1000
Copy link
Author

Seems like I can use following JsonDiffOptions to control array-comparison:

  • ArrayObjectItemKeyFinder
  • ArrayObjectItemMatchByPosition and maybe
  • PropertyFilter

Looks promising, the following works as expected (I've changed bar1 to ID since this would be my real-world example, if for any reason the ID is missing I'm falling back with ArrayObjectItemMatchByPosition=true):

var node1 = JsonNode.Parse("{\"foo\":[ {\"ID\": \"res1\", \"bar2\": \"res3\"} ]}");
var node2 = JsonNode.Parse("{\"foo\":[ {\"ID\": \"res1\", \"bar2\": \"res4\"} ]}");

var opt = new JsonDiffOptions
{
    ArrayObjectItemKeyFinder = ArrayObjectItemKeyFinder,
    ArrayObjectItemMatchByPosition = true
};

object? ArrayObjectItemKeyFinder(JsonNode? node, int index)
{
    if (node is JsonObject obj && obj.TryGetPropertyValue("ID", out var value))
    {
        return value?.GetValue<string>() ?? "";
    }
    return null;
}

var x = JsonDiffPatcher.Diff(node1, node2, new JsonPatchDeltaFormatter(), opt);
Console.WriteLine(x);

@Cyber1000
Copy link
Author

Did I miss out any documentation about this?

@Cyber1000
Copy link
Author

Yes works this way:

  • One suggestion: JsonPatchDeltaFormatter should have overridable strings like PropertyNameOperation and JsonPatchDeltaFormatter.PropertyPathScope should be protected instead of private. I had a small (project-specific) change and needed to copy more than I should.

@weichch
Copy link
Owner

weichch commented Feb 13, 2023

What's the best method to check arrays recursively (the way I want)?

I think you've pretty much found the way.

If you have a look at the JsonDiffOptions, there are 3 ways you can use:

  • ArrayItemMatcher which deals with the diff context
  • ArrayObjectItemKeyFinder which finds the key/id of an object or array
  • ArrayObjectItemMatchByPosition which indicates whether to compare two objects or arrays by their position

By default if the two objects are not deeply equal to each other, then they are considered totally not equal and therefore the remove/add (arguably update) result. To forcibly get a diff of array items, you need to implement an "object comparer" to compare them if they are not deeply equal, similar to an object hash function.

There probably should be some Wiki documentation about array diffs.

JsonPatchDeltaFormatter.PropertyPathScope should be protected instead of private

I agree. The reason for it being a private is because the implementation of the class is actually a combination of partial JSON Pointer and a pointer scope at the moment. The JSON Pointer implementation should ideally be a public type. I did not want to expose this type until it is properly refactored otherwise it might be hard to change the implementation.

@weichch weichch added documentation Improvements or additions to documentation question Further information is requested labels Feb 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants