Skip to content

Commit 73ca275

Browse files
authored
Merge pull request #35612 from dotnet/main
2 parents 4bd3048 + 6d27bb8 commit 73ca275

File tree

1 file changed

+58
-56
lines changed

1 file changed

+58
-56
lines changed

aspnetcore/release-notes/aspnetcore-10/includes/jsonPatch.md

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,89 @@
1-
### New JsonPatch Implementation with System.Text.Json
1+
### New JSON Patch implementation with `System.Text.Json`
22

33
**[JSON Patch](https://jsonpatch.com/)**:
44

55
* Is a standard format for describing changes to apply to a JSON document.
6-
* Is defined in [RFC 6902] and is widely used in RESTful APIs to perform partial updates to JSON resources.
6+
* Is defined in RFC 6902 and is widely used in RESTful APIs to perform partial updates to JSON resources.
77
* Represents a sequence of operations (for example, Add, Remove, Replace, Move, Copy, Test) that can be applied to modify a JSON document.
88

99
In web apps, JSON Patch is commonly used in a PATCH operation to perform partial updates of a resource. Rather than sending the entire resource for an update, clients can send a JSON Patch document containing only the changes. Patching reduces payload size and improves efficiency.
1010

1111
[RFC 6902]: https://tools.ietf.org/html/rfc6902
1212

13-
This release introduces a new implementation of `JsonPatch` based on `System.Text.Json` serialization. This feature:
13+
This release introduces a new implementation of <xref:Microsoft.AspNetCore.JsonPatch> based on <xref:System.Text.Json?displayProperty=fullName> serialization. This feature:
1414

1515
* Aligns with modern .NET practices by leveraging the `System.Text.Json` library, which is optimized for .NET.
1616
* Provides improved performance and reduced memory usage compared to the legacy `Newtonsoft.Json`-based implementation.
1717

18-
The following benchmarks compare the performance of the new `System.Text.Json` implementation with the legacy `Newtonsoft.Json` implementation:
18+
The following benchmarks compare the performance of the new `System.Text.Json` implementation with the legacy `Newtonsoft.Json` implementation.
1919

20-
| Scenario | Implementation | Mean | Allocated Memory |
21-
|----------------------------|------------------------|------------|------------------|
22-
| **Application Benchmarks** | Newtonsoft.JsonPatch | 271.924 µs | 25 KB |
23-
| | System.Text.JsonPatch | 1.584 µs | 3 KB |
24-
| **Deserialization Benchmarks** | Newtonsoft.JsonPatch | 19.261 µs | 43 KB |
25-
| | System.Text.JsonPatch | 7.917 µs | 7 KB |
20+
| Scenario | Implementation | Mean | Allocated Memory |
21+
| ------------------------------ | --------------------- | :--------: | :--------------: |
22+
| **Application Benchmarks** | Newtonsoft.JsonPatch | 271.924 µs | 25 KB |
23+
| | System.Text.JsonPatch | 1.584 µs | 3 KB |
24+
| **Deserialization Benchmarks** | Newtonsoft.JsonPatch | 19.261 µs | 43 KB |
25+
| | System.Text.JsonPatch | 7.917 µs | 7 KB |
2626

2727
These benchmarks highlight significant performance gains and reduced memory usage with the new implementation.
2828

2929
Notes:
30-
* The new implementation isn't a drop-in replacement for the legacy implementation. In particular:
31-
* The new implementation doesn't support dynamic types, for example, [`ExpandoObject`](/dotnet/api/system.dynamic.expandoobject).
30+
* The new implementation isn't a drop-in replacement for the legacy implementation. In particular, the new implementation doesn't support dynamic types, for example, <xref:System.Dynamic.ExpandoObject>.
3231
* The JSON Patch standard has ***inherent security risks***. Since these risks are inherent to the JSON Patch standard, the new implementation ***doesn't attempt to mitigate inherent security risks***. It's the responsibility of the developer to ensure that the JSON Patch document is safe to apply to the target object. For more information, see the [Mitigating Security Risks](#mitigating-security-risks) section.
3332

3433
#### Usage
3534

36-
To enable JSON Patch support with `System.Text.Json`, install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch/) NuGet package.
35+
To enable JSON Patch support with `System.Text.Json`, install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package.
3736

38-
```sh
37+
```dotnetcli
3938
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
4039
```
4140

42-
This package provides a `JsonPatchDocument#### T>` class to represent a JSON Patch document for objects of type `T` and custom logic for serializing and deserializing JSON Patch documents using `System.Text.Json`. The key method of the `JsonPatchDocument<T>` class is `ApplyTo`, which applies the patch operations to a target object of type `T`.
41+
This package provides a `JsonPatchDocument<T>` class to represent a JSON Patch document for objects of type `T` and custom logic for serializing and deserializing JSON Patch documents using `System.Text.Json`. The key method of the `JsonPatchDocument<T>` class is `ApplyTo`, which applies the patch operations to a target object of type `T`.
4342

4443
The following examples demonstrate how to use the `ApplyTo` method to apply a JSON Patch document to an object.
4544

46-
#### Example: Applying a JsonPatchDocument
45+
#### Example: Applying a `JsonPatchDocument`
4746

4847
The following example demonstrates:
4948

5049
1. The `add`, `replace`, and `remove` operations.
5150
2. Operations on nested properties.
5251
3. Adding a new item to an array.
53-
4. Using a JSON String Enum Converter in a JSON patch document.
52+
4. Using a JSON String Enum Converter in a JSON Patch document.
5453

5554
```csharp
5655
// Original object
5756
var person = new Person {
58-
FirstName = "John",
59-
LastName = "Doe",
60-
Email = "johndoe@gmail.com",
61-
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
62-
Address = new Address
63-
{
64-
Street = "123 Main St",
65-
City = "Anytown",
66-
State = "TX"
67-
}
57+
FirstName = "John",
58+
LastName = "Doe",
59+
Email = "johndoe@gmail.com",
60+
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
61+
Address = new Address
62+
{
63+
Street = "123 Main St",
64+
City = "Anytown",
65+
State = "TX"
66+
}
6867
};
6968

70-
// Raw JSON patch document
71-
string jsonPatch = """
69+
// Raw JSON Patch document
70+
var jsonPatch = """
7271
[
73-
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
74-
{ "op": "remove", "path": "/Email"},
75-
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
76-
{ "op": "add", "path": "/PhoneNumbers/-", "value": { "Number": "987-654-3210",
77-
"Type": "Work" } }
72+
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
73+
{ "op": "remove", "path": "/Email"},
74+
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
75+
{
76+
"op": "add",
77+
"path": "/PhoneNumbers/-",
78+
"value": { "Number": "987-654-3210", "Type": "Work" }
79+
}
7880
]
7981
""";
8082

81-
// Deserialize the JSON patch document
83+
// Deserialize the JSON Patch document
8284
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
8385

84-
// Apply the JSON patch document
86+
// Apply the JSON Patch document
8587
patchDoc!.ApplyTo(person);
8688

8789
// Output updated object
@@ -112,46 +114,46 @@ Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
112114

113115
The `ApplyTo` method generally follows the conventions and options of `System.Text.Json` for processing the `JsonPatchDocument`, including the behavior controlled by the following options:
114116

115-
* `NumberHandling`: Whether numeric properties can be read from strings.
117+
* `NumberHandling`: Whether numeric properties are read from strings.
116118
* `PropertyNameCaseInsensitive`: Whether property names are case-sensitive.
117119

118120
Key differences between `System.Text.Json` and the new `JsonPatchDocument<T>` implementation:
119121

120-
* The runtime type of the target object, not the declared type, determines which properties `ApplyTo` patches.
122+
* The runtime type of the target object, not the declared type, determines which properties `ApplyTo` patches.
121123
* `System.Text.Json` deserialization relies on the declared type to identify eligible properties.
122124

123-
#### Example: Applying a JsonPatchDocument with error handling
125+
#### Example: Applying a `JsonPatchDocument` with error handling
124126

125127
There are various errors that can occur when applying a JSON Patch document. For example, the target object may not have the specified property, or the value specified might be incompatible with the property type.
126128

127-
JSON `Patch` also supports the `test` operation. The `test` operation checks if a specified value is equal to the target property, and if not, returns an error.
129+
JSON Patch also supports the `test` operation. The `test` operation checks if a specified value is equal to the target property, and if not, returns an error.
128130

129131
The following example demonstrates how to handle these errors gracefully.
130132

131-
> [!Important]
133+
> [!IMPORTANT]
132134
> The object passed to the `ApplyTo` method is modified in place. It is the caller's responsiblity to discard these changes if any operation fails.
133135
134136
```csharp
135137
// Original object
136138
var person = new Person {
137-
FirstName = "John",
138-
LastName = "Doe",
139-
Email = "johndoe@gmail.com"
139+
FirstName = "John",
140+
LastName = "Doe",
141+
Email = "johndoe@gmail.com"
140142
};
141143

142-
// Raw JSON patch document
143-
string jsonPatch = """
144+
// Raw JSON Patch document
145+
var jsonPatch = """
144146
[
145-
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
146-
{ "op": "test", "path": "/FirstName", "value": "Jane" },
147-
{ "op": "replace", "path": "/LastName", "value": "Smith" }
147+
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
148+
{ "op": "test", "path": "/FirstName", "value": "Jane" },
149+
{ "op": "replace", "path": "/LastName", "value": "Smith" }
148150
]
149151
""";
150152

151-
// Deserialize the JSON patch document
153+
// Deserialize the JSON Patch document
152154
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
153155

154-
// Apply the JSON patch document, catching any errors
156+
// Apply the JSON Patch document, catching any errors
155157
Dictionary<string, string[]>? errors = null;
156158
patchDoc!.ApplyTo(person, jsonPatchError =>
157159
{
@@ -191,7 +193,7 @@ Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
191193
When using the `Microsoft.AspNetCore.JsonPatch.SystemTextJson` package, it's critical to understand and mitigate potential security risks. The following sections outline the identified security risks associated with JSON Patch and provide recommended mitigations to ensure secure usage of the package.
192194

193195
> [!IMPORTANT]
194-
> ***This is not an exhaustive list of threats.*** app developers must conduct their own threat model reviews to determine an app-specific comprehensive list and come up with appropriate mitigations as needed. For example, apps which expose collections to patch operations should consider the potential for algorithmic complexity attacks if those operations insert or remove elements at the beginning of the collection.
196+
> ***This is not an exhaustive list of threats.*** App developers must conduct their own threat model reviews to determine an app-specific comprehensive list and come up with appropriate mitigations as needed. For example, apps which expose collections to patch operations should consider the potential for algorithmic complexity attacks if those operations insert or remove elements at the beginning of the collection.
195197
196198
By running comprehensive threat models for their own apps and addressing identified threats while following the recommended mitigations below, consumers of these packages can integrate JSON Patch functionality into their apps while minimizing security risks.
197199

@@ -207,15 +209,15 @@ Consumers of these packages can integrate JSON Patch functionality into their ap
207209
* **Impact**: Potential Out-Of-Memory (OOM) conditions, causing service disruptions.
208210
* **Mitigation**:
209211
* Validate incoming JSON Patch documents for size and structure before calling `ApplyTo`.
210-
* The validation needs to be app specific, but an example validation can look similar to the following:
212+
* The validation must be app specific, but an example validation can look similar to the following:
211213

212214
```csharp
213215
public void Validate(JsonPatchDocument<T> patch)
214216
{
215217
// This is just an example. It's up to the developer to make sure that
216-
// this case is handled properly, based on the app needs.
218+
// this case is handled properly, based on the app's requirements.
217219
if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
218-
> MaxCopyOperationsCount)
220+
> MaxCopyOperationsCount)
219221
{
220222
throw new InvalidOperationException();
221223
}
@@ -224,7 +226,7 @@ public void Validate(JsonPatchDocument<T> patch)
224226

225227
##### Business Logic Subversion
226228

227-
* **Scenario**: Patch operations can manipulate fields with implicit invariants, (e.g., internal flags, IDs, or computed fields), violating business constraints.
229+
* **Scenario**: Patch operations can manipulate fields with implicit invariants, (for example, internal flags, IDs, or computed fields), violating business constraints.
228230
* **Impact**: Data integrity issues and unintended app behavior.
229231
* **Mitigation**:
230232
* Use POCO objects with explicitly defined properties that are safe to modify.

0 commit comments

Comments
 (0)