-
Notifications
You must be signed in to change notification settings - Fork 369
Add migrating-newtonsoft-to-system-text-json skill #200
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
Changes from all commits
e47051f
26d849b
3f5f6f8
eae7189
36b50cc
700e751
f9c0d31
c316e8f
1f79841
def169f
b9e8364
29075f6
ff2fb3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,185 @@ | ||||||||||||||||||||||
| --- | ||||||||||||||||||||||
| name: migrating-newtonsoft-to-system-text-json | ||||||||||||||||||||||
| description: > | ||||||||||||||||||||||
| Migrate from Newtonsoft.Json to System.Text.Json, handling behavioral differences, | ||||||||||||||||||||||
| custom converters, and common breaking changes. Use when converting a project from | ||||||||||||||||||||||
| Newtonsoft.Json (Json.NET) to the built-in System.Text.Json serializer. | ||||||||||||||||||||||
|
Comment on lines
+4
to
+6
|
||||||||||||||||||||||
| Migrate from Newtonsoft.Json to System.Text.Json, handling behavioral differences, | |
| custom converters, and common breaking changes. Use when converting a project from | |
| Newtonsoft.Json (Json.NET) to the built-in System.Text.Json serializer. | |
| USE FOR: Migrating a project from Newtonsoft.Json (Json.NET) to | |
| System.Text.Json; updating serialization and deserialization code to use the | |
| built-in serializer; handling behavioral differences, custom converters, and | |
| common breaking changes encountered during the migration. DO NOT USE FOR: | |
| General JSON serialization guidance unrelated to migration; projects that are | |
| staying on Newtonsoft.Json; unrelated .NET upgrade tasks that do not involve | |
| replacing Newtonsoft.Json with System.Text.Json. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding an explicit instruction to the agent that it should be adding baseline serialization tests for the application models while performing the migration.
Copilot
AI
Apr 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mapping [JsonProperty(Required = Required.Always)] → [JsonRequired] is not a drop-in equivalent: Newtonsoft's Required.Always enforces both presence and non-null (and has nuanced behavior with defaults), while System.Text.Json's [JsonRequired] primarily enforces presence and interacts differently with nullable annotations/required members. Please add a brief note clarifying the semantic differences and suggesting the usual alternatives (e.g., required members + validation) so readers don't assume identical runtime behavior.
Copilot
AI
Apr 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line implies JsonElement is IDisposable ("JsonDocument/JsonElement (IDisposable ...)"). In System.Text.Json only JsonDocument is disposable; JsonElement becomes invalid once its owning JsonDocument is disposed. Reword to avoid suggesting that JsonElement itself should/can be disposed, and keep the guidance about cloning when the document lifetime is shorter than the element lifetime.
| For **read-only** scenarios, use `JsonDocument`/`JsonElement` (IDisposable, must clone if keeping past dispose). | |
| For **read-only** scenarios, use `JsonDocument`/`JsonElement`; `JsonDocument` is `IDisposable`, and if a `JsonElement` must outlive its owning document, clone it before disposing the document. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,124 @@ | ||||||
| scenarios: | ||||||
| - name: "Migrate model with Newtonsoft.Json attributes to System.Text.Json" | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we add evals covering more scenaria? Custom converters? Naming policies? etc? Consider mining real-world snippets either from dotnet org repos or public github repos in general (e.g. via grep.app) |
||||||
| prompt: | | ||||||
| I'm migrating our ASP.NET Core 8 project from Newtonsoft.Json to System.Text.Json. Here's a model class that uses Newtonsoft attributes and a built-in converter. Convert this to System.Text.Json: | ||||||
|
|
||||||
|
danmoseley marked this conversation as resolved.
|
||||||
| ```csharp | ||||||
| using Newtonsoft.Json; | ||||||
| using Newtonsoft.Json.Converters; | ||||||
| using Newtonsoft.Json.Linq; | ||||||
|
|
||||||
| public class Order | ||||||
| { | ||||||
| [JsonProperty("order_id")] | ||||||
| public int Id { get; set; } | ||||||
|
|
||||||
| [JsonProperty(Required = Required.Always)] | ||||||
| public string CustomerName { get; set; } | ||||||
|
|
||||||
| [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] | ||||||
| public string? Notes { get; set; } | ||||||
|
|
||||||
| [JsonConverter(typeof(StringEnumConverter))] | ||||||
| public OrderStatus Status { get; set; } | ||||||
|
|
||||||
| [JsonExtensionData] | ||||||
| public Dictionary<string, JToken>? AdditionalData { get; set; } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Also show me how to configure the JSON options globally for Newtonsoft compatibility (case insensitivity, trailing commas, number-from-string coercion). What else should I watch out for when completing this migration? | ||||||
| reject_tools: | ||||||
| - "bash" | ||||||
| - "create_file" | ||||||
| - "edit" | ||||||
| assertions: | ||||||
| - type: "output_contains" | ||||||
| value: "JsonPropertyName" | ||||||
| - type: "output_matches" | ||||||
| pattern: "(JsonIgnore.*WhenWritingNull|JsonIgnoreCondition)" | ||||||
| - type: "output_matches" | ||||||
| pattern: "(PropertyNameCaseInsensitive|CamelCase|PropertyNamingPolicy)" | ||||||
| - type: "output_matches" | ||||||
| pattern: "(JsonElement|JsonNode|JsonObject)" | ||||||
| rubric: | ||||||
| - "Replaced [JsonProperty(\"order_id\")] with [JsonPropertyName(\"order_id\")]" | ||||||
| - "Replaced NullValueHandling.Ignore with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]" | ||||||
| - "Replaced [JsonConverter(typeof(StringEnumConverter))] with System.Text.Json equivalent (JsonStringEnumConverter)" | ||||||
| - "Changed [JsonExtensionData] Dictionary value type from JToken to a System.Text.Json type (JsonElement, JsonNode, or JsonObject)" | ||||||
| - "Configured PropertyNameCaseInsensitive = true to match Newtonsoft default case-insensitive behavior" | ||||||
| - "Warned about security trade-offs or behavioral differences when loosening System.Text.Json's strict defaults (e.g., comments enabling desync attacks, case insensitivity widening input surface)" | ||||||
| - "Mentioned removing the Newtonsoft.Json or Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet packages as a migration step" | ||||||
| - "Noted that NullValueHandling.Ignore is NOT the Newtonsoft default — DefaultIgnoreCondition.WhenWritingNull should only be applied globally if the existing code explicitly used NullValueHandling.Ignore in settings" | ||||||
| timeout: 120 | ||||||
|
|
||||||
| - name: "Review a partially completed Newtonsoft.Json to System.Text.Json migration for correctness" | ||||||
| prompt: | | ||||||
| I migrated our .NET 8 Web API from Newtonsoft.Json to System.Text.Json over the weekend. It compiles and basic tests pass, but I want a thorough review before we ship. Can you review my migration and point out any issues, risks, or things I missed? | ||||||
|
|
||||||
| **Before (Newtonsoft.Json setup in Program.cs):** | ||||||
| ```csharp | ||||||
| builder.Services.AddControllers() | ||||||
| .AddNewtonsoftJson(options => | ||||||
| { | ||||||
| options.SerializerSettings.Converters.Add( | ||||||
| new StringEnumConverter(new CamelCaseNamingStrategy())); | ||||||
| }); | ||||||
| ``` | ||||||
|
|
||||||
| **After (my System.Text.Json migration in Program.cs):** | ||||||
| ```csharp | ||||||
| using System.Text.Json; | ||||||
| using System.Text.Json.Serialization; | ||||||
| using Newtonsoft.Json; // keeping this — we still use JObject in one helper | ||||||
|
||||||
| using Newtonsoft.Json; // keeping this — we still use JObject in one helper | |
| using Newtonsoft.Json.Linq; // keeping this — we still use JObject in one helper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description lists files under
src/dotnet/..., but the changes in this PR add the skill underplugins/dotnet-upgrade/...and tests undertests/dotnet-upgrade/.... Please align the PR description's file list with the actual paths to avoid confusion for reviewers and future archaeology.