I have the following environment and version of .NET Core
.NET SDK (reflecting any global.json):
Version: 5.0.100
Commit: 5044b93829
Runtime Environment:
OS Name: Mac OS X
OS Version: 11.0
OS Platform: Darwin
RID: osx.11.0-x64
Base Path: /usr/local/share/dotnet/sdk/5.0.100/
Using the following code and endpoint.
public class Trie
{
public string Id { get; set; }
public Dictionary<string, TrieNode> Nodes { get; set; }
}
public class TrieNode
{
public string Name { get; set; }
}
public async Task<IActionResult> Patch(string id, [FromBody] JsonPatchDocument<Trie> patchDoc)
{
var trie = [get from database]
patchDoc.ApplyTo(trie, ModelState);
}
When I use the following op:
[
{
"op": "add",
"path": "/nodes/-", "value": { "/": { "name": "hello, world" } }
}
]
an item is added to the dictionary like this:
"Nodes": {
"-": {
"Name": null
}
}
If I use the following syntax for the op
[
{
"op": "add",
"path": "/nodes", "value": { "/": { "name": "hello, world" } }
}
]
The item is added like this:
"Nodes": {
"/": {
"Name": "hello, world"
}
}
But when I try to add a new item with another key like this:
[
{
"op": "add",
"path": "/nodes", "value": { "/my-key": { "name": "hello, world" } }
}
]
The item is not added, it's replaces the first entry so the result looks like this:
"Nodes": {
"/my-key": {
"Name": "hello, world"
}
}
How can I add, remove, replace, copy, move entries in a Dictionary<string, TrieNode>?
The format you use will set the whole property value (to a dictionary containing only one entry). That's why you see it's added but not updated.
For updating an entry via the key, looks like this is not documented anywhere. The actual syntax for updating a key (either adding or updating an existing one) is like this:
/path-to-dictionary/string-key
The path above targets existing entry for remove
as well.
The value
then should be just the entry value (not including key as in your code), like this:
{ "name": "hello, world" }
Here's the sample formats for different operations:
Add or Update:
[
{
"op": "add",
"path": "/nodes/my-key",
"value": { "name": "hello, world" }
}
]
Remove:
[
{
"op": "remove",
"path": "/nodes/my-key"
}
]
Replace: This is effectively like add
for existing key.
[
{
"op": "replace",
"path": "/nodes/my-key",
"value": { "name": "hello, world" }
}
]
Copy:
[
{
"op": "copy",
"from": "/nodes/my-existing-key",
"path": "/nodes/my-key"
}
]
Move:
[
{
"op": "move",
"from": "/nodes/my-existing-key",
"path": "/nodes/my-key"
}
]
NOTE: The path format for dictionary requires the dictionary property to be non-null. Otherwise the path will be considered as non-existed and an error will be thrown with some message like this:
... the target location specified by path '/nodes/my-key' was not found
With the format used in your code, the whole dictionary property will be set. That's why there is always one entry remained (the final one overwrites all the others including the existing entries). With this note, you should ensure that your dictionary property is initialized first, like this:
if(trie.Nodes == null){
trie.Nodes = new Dictionary<string, TrieNode>();
}
patchDoc.ApplyTo(trie, ModelState);
About the dictionary key type:
Here in your example, the key type is string
, but it can be any type as long as you have an associated TypeConverter
for that type to work correctly. E.g: it can be int
so you can use int keys normally, it can be DateTime
so you can use key formats like yyyy-MM-dd
(to use in the path) ...