|
| 1 | +# JsonPatch |
| 2 | + |
| 3 | +This is a new experimental type for applying JSON patches to a model that includes this as a property |
| 4 | +or for dealing with raw UTF8 JSON directly. The examples in this document show using outside |
| 5 | +of being attached to a model for simplicity. |
| 6 | + |
| 7 | +There are 5 main APIs for library users, `Set`, `Append`, `Get`, `Remove`, `SetNull`. Each of these have overloads for different |
| 8 | +value types to get in and out of the JsonPatch object. |
| 9 | + |
| 10 | +All of these APIs take in `ReadOnlySpan<byte>` which should be UTF8 representation of a single target JSON Path following [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535). |
| 11 | +Even if a filter selector results in single target they are currently not supported such as `$.x.y[[email protected]='foo']` where the array at y only contains one element with the name foo. |
| 12 | + |
| 13 | +This type is intended to follow [RFC 6902](https://www.rfc-editor.org/rfc/rfc6902) although currently does not have Move, Copy, or Test implemented. |
| 14 | +The default `ToString` implementation will print in RFC 6902 format, but will take an |
| 15 | +optional format string to print in other formats in the future. The only other format currently supported is `J` which will print the current model state |
| 16 | +after applying the patches in RFC 8259 format. |
| 17 | + |
| 18 | +## Library user APIs |
| 19 | + |
| 20 | +### Set |
| 21 | + |
| 22 | +```c# |
| 23 | +JsonPatch jp = new(); |
| 24 | +jp.Set("$.x"u8, 5); |
| 25 | +Console.WriteLine(jp); // [{"op":"add","path":"/x","value":5}] |
| 26 | +Console.WriteLine(jp.ToString("J")); // {"x":5} |
| 27 | +``` |
| 28 | + |
| 29 | +Set will project the json structure from the path so that users do not have to set each layer one by one |
| 30 | + |
| 31 | +```c# |
| 32 | +JsonPatch jp = new(); |
| 33 | +jp.Set("$.x.y[2].z"u8, 5); |
| 34 | +Console.WriteLine(jp); // [{"op":"add","path":"/x","value":{"y":[null,null,{"z":5}]}}] |
| 35 | +Console.WriteLine(jp.ToString("J")); // {"x":{"y":[null,null,{"z":5}]}} |
| 36 | +``` |
| 37 | + |
| 38 | +The nulls are inserted to fill up to the index in the path if it doesn't exist yet. |
| 39 | + |
| 40 | +You can also pass json structures yourself to Set if you already have a json byte array |
| 41 | + |
| 42 | +```c# |
| 43 | +JsonPatch jp = new(); |
| 44 | +jp.Set("$.x"u8, "{\"y\":[null,null,{\"z\":5}]}"u8); |
| 45 | +Console.WriteLine(jp); // [{"op":"add","path":"/x","value":{"y":[null,null,{"z":5}]}}] |
| 46 | +Console.WriteLine(jp.ToString("J")); // {"x":{"y":[null,null,{"z":5}]}} |
| 47 | +``` |
| 48 | + |
| 49 | +### Append |
| 50 | + |
| 51 | +Append will add to an existing array. |
| 52 | + |
| 53 | +```c# |
| 54 | +JsonPatch jp = new("[1,2,3]"u8.ToArray()); |
| 55 | +jp.Append("$"u8, 4); |
| 56 | +Console.WriteLine(jp); // [{"op":"add","path":"/-","value":4}] |
| 57 | +Console.WriteLine(jp.ToString("J")); // [1,2,3,4] |
| 58 | +``` |
| 59 | + |
| 60 | +Append will also utilize json projection if the path doesn't exist yet. |
| 61 | + |
| 62 | +```c# |
| 63 | +JsonPatch jp = new(); |
| 64 | +jp.Append("$.x[1]"u8, 1); |
| 65 | +Console.WriteLine(jp); // [{"op":"add","path":"/x","value":[null,[1]]}] |
| 66 | +Console.WriteLine(jp.ToString("J")); // {"x":[null,[1]]} |
| 67 | +``` |
| 68 | + |
| 69 | +### Get |
| 70 | + |
| 71 | +Get allows a user to pull information from the patches that have been inserted so far or from the |
| 72 | +original json used to construct the JsonPatch. |
| 73 | + |
| 74 | +Get allows you to pull primitive values as well as raw json. For the primitive values it has an overload GetNullableValue to allow for things like int?. |
| 75 | + |
| 76 | +```c# |
| 77 | +JsonPatch jp = new("{\"x\":{\"y\":[null,null,{\"z\":5}]}}"u8.ToArray()); |
| 78 | +int value = jp.GetInt32("$.x.y[2].z"u8); |
| 79 | +Console.WriteLine(value); // 5 |
| 80 | +
|
| 81 | +BinaryData json = jp.GetJson("$.x.y"); |
| 82 | +Console.WriteLine(json); // [null,null,{"z":5}] |
| 83 | +``` |
| 84 | + |
| 85 | +### Remove |
| 86 | + |
| 87 | +Remove deletes a path from the payload which is distinct from setting something to null. |
| 88 | + |
| 89 | +```c# |
| 90 | +JsonPatch jp = new("{\"x\":{\"y\":[null,null,{\"z\":5}]}}"u8.ToArray()); |
| 91 | +jp.Remove("$.x.y[1]"u8); |
| 92 | +Console.WriteLine(jp); // [{"op":"remove","path":"/x/y/1"}] |
| 93 | +Console.WriteLine(jp.ToString("J")); // {"x":{"y":[null,{"z":5}]}} |
| 94 | +``` |
| 95 | + |
| 96 | +### SetNull |
| 97 | + |
| 98 | +Sets explicit null at a specific json path. The reason this is separate from Set is in order to accept `null` we would need an |
| 99 | +overload with object which introduces lots of failure cases where a library user might try to pass in a random object which |
| 100 | +we cannot deal with generically. |
| 101 | + |
| 102 | +```c# |
| 103 | +JsonPatch jp = new("{\"x\":{\"y\":[null,null,{\"z\":5}]}}"u8.ToArray()); |
| 104 | +jp.SetNull("$.x.y[2]"u8); |
| 105 | +Console.WriteLine(jp); // [{"op":"replace","path":"/x/y/2","value":null}] |
| 106 | +Console.WriteLine(jp.ToString("J")); // {"x":{"y":[null,null,null]}} |
| 107 | +``` |
0 commit comments