Something wrong in handling list
smartepsh opened this issue · comments
Describe the bug
- The array is a legal json structured type, but can't apply a patch for a list directly. (Maybe it's your feature...)
- Can't handle list well. (see examples below)
- (Suggestion) Swap arguments of
Jsonpatch.apply_patch/2
so that the pipeline can be used.map |> Jsonpatch.apply_patch!(diff_1) |> Jsonpatch.apply_patch!(diff_2)
To Reproduce
Example 1:
base = %{list: [1, 2, 3]}
target = %{list: [2, 3, 1]}
diff = Jsonpatch.diff(base, target)
Jsonpatch.apply_patch!(diff, base)
Example 2:
base = %{list: [%{a: 1}, %{b: 2}, %{c: 3}]}
target = %{list: [%{b: 2}, %{c: 3}, %{a: 1}]}
diff = Jsonpatch.diff(base, target)
Jsonpatch.apply_patch!(diff, base)
Expected behavior
I hope the result of Jsonpatch.after_patch!(diff, base)
is equal to target
.
Screenshots
Example 1:
Example 2:
Desktop (please complete the following information):
- Elixir - 1.13.3
- Erlang - 24.2.1
- System - MacOS 12.3.1
Thanks for the report!
(Maybe it's your feature...)
Indeed. This is my fault that there is no hint in the documentation, yet. The issue is the map and not the list because it is atom based map. Atoms as keys are not supported, yet.
Why? So far, I saw only JSON parser that create maps with strings as keys because they are safer in case not trusted JSON is parsed. Else it would be possible to trigger the creation of atoms from the outside (when we are talking about REST interfaces for example). I followed this philosophy but never stated it anywhere.
(The path of the patch operation must be converted into an atom.)
But today I think it should be possible to use something like an option. Example:
Jsonpatch.apply_patch!(diff, base, allow_atoms: true)
In addition the error message could be more useful.
- Improve error message in case of maps with atom keys
- Mention danger of using maps with atom keys
- Add option to allow the creation of atoms
I have no spare time this week. If its urgent or you are interest in solving it - feel free to create a PR. 🙏
Thanks for the reply.
I hadn't noticed before that this was a problem with atom keys 😮💨 I have another example about the list with the string key's map.
Example 3
base = %{"key" => []}
target = %{"key" => [1, 2, 3]}
diff = Jsonpatch.diff(base, target)
Jsonpatch.apply_patch!(diff, base)
It restores only one element of the list. I think it's about the diff order.
You are right. Jsonpatch.apply_patch
should follow the order.
On the other hand, the result of the diff should be in the same orders as they are detected. That should be changed.
- Produce diff in the same order as they are detected
You are right.
Jsonpatch.apply_patch
should follow the order.On the other hand, the result of the diff should be in the same orders as they are detected. That should be changed.
- Produce diff in the same order as they are detected
This is more important to us, so I took care of that first. Please. #9
Nice - thanks for the contribution! I merged it and published version 0.12.1. :-)
The progress looks good so far:
iex(1)> base = %{list: [1, 2, 3]}
%{list: [1, 2, 3]}
iex(2)> target = %{list: [2, 3, 1]}
%{list: [2, 3, 1]}
iex(3)> diff = Jsonpatch.diff(base, target)
[
%Jsonpatch.Operation.Replace{path: "/list/0", value: 2},
%Jsonpatch.Operation.Replace{path: "/list/1", value: 3},
%Jsonpatch.Operation.Replace{path: "/list/2", value: 1}
]
iex(4)> Jsonpatch.apply_patch!(diff, base, keys: :atoms)
%{list: [2, 3, 1]}
I think only tests are missing.
The change is now in main and released under version 0.13.0.