Start Debugging

System.Text.Json vs Newtonsoft.Json in 2026: which should you pick?

Pick System.Text.Json for new .NET 11 code: it ships in-box, is roughly 2x faster, and is the only one that works with Native AOT. Reach for Newtonsoft.Json only for JSONPath, TypeNameHandling, or genuinely lenient JSON.

If you are starting new code on .NET 11 in 2026, use System.Text.Json. It ships in-box with the runtime, serializes roughly twice as fast with a fraction of the allocations, and is the only one of the two that runs under Native AOT. Reach for Newtonsoft.Json only when you depend on a feature System.Text.Json still does not have: JSONPath queries, TypeNameHandling-style type embedding, or parsing genuinely malformed JSON (single quotes, unquoted keys). Both libraries are alive in 2026, but they are no longer equals for greenfield work.

Every example here targets <TargetFramework>net11.0</TargetFramework> with the .NET 11 SDK and C# 14. System.Text.Json is the in-box version that ships with .NET 11. Newtonsoft.Json refers to version 13.0.4, released 2025-12-30, the current stable on NuGet.

The feature matrix at a glance

This is the table you came for. It is the practical version of the official Microsoft migration guide, reduced to the decisions that actually change which package you reference.

ConcernSystem.Text.Json (.NET 11)Newtonsoft.Json 13.0.4
Ships in-boxYes, part of the runtimeNo, NuGet package
Throughput (serialize)Baseline, fastest~2x slower
AllocationsSpan-based, lowHigher
Native AOT / trimmingYes, via source generatorNo
Default leniencyStrict (RFC 8259)Lenient
Comments / trailing commasOpt-inOn by default
Case-insensitive matchingOpt-in (on in ASP.NET Core)On by default
Polymorphic (de)serializationYes, since .NET 7 ([JsonDerivedType])Yes (TypeNameHandling)
TypeNameHandling.All (embed CLR type)No, by designYes
JSONPath / SelectTokenNoYes
LINQ-to-JSON DOMJsonNode / JsonDocumentJObject / JArray
DataTable, ExpandoObject, BigIntegerCustom converter requiredBuilt-in
Single quotes, unquoted keysRejected, by designAccepted
Maintenance statusActive developmentMaintenance mode
LicenseMITMIT

The headline is that the rows where Newtonsoft.Json still wins are narrow and specific, while the rows where System.Text.Json wins (in-box, AOT, speed) apply to almost every new project.

When to pick System.Text.Json

Pick it as the default for anything new on .NET 11. Concretely:

A source-generated context is the pattern that unlocks AOT and the fastest startup:

// .NET 11, C# 14 - compile-time metadata, no runtime reflection
using System.Text.Json;
using System.Text.Json.Serialization;

[JsonSerializable(typeof(WeatherForecast))]
public partial class AppJsonContext : JsonSerializerContext;

var forecast = new WeatherForecast(DateOnly.FromDateTime(DateTime.Now), 22, "Mild");

// Pass the generated TypeInfo, not the raw type, to stay reflection-free
string json = JsonSerializer.Serialize(forecast, AppJsonContext.Default.WeatherForecast);

public record WeatherForecast(DateOnly Date, int TemperatureC, string Summary);

System.Text.Json has also closed most of the historical gaps. Since .NET 7 it does polymorphic (de)serialization through [JsonDerivedType], and .NET 9 added several long-requested options: RespectNullableAnnotations to honor non-nullable reference types, the JsonStringEnumMemberName attribute to rename enum values, and JsonSchemaExporter to produce a JSON schema from a .NET type. .NET 10 added direct deserialization from a PipeReader and a one-line strict preset:

// .NET 10 and later - the strict, spec-compliant defaults in one preset
var options = JsonSerializerOptions.Strict;

// .NET 10 and later - deserialize straight from a PipeReader, no stream adapter
WeatherForecast? f = await JsonSerializer.DeserializeAsync<WeatherForecast>(pipeReader);

When to pick Newtonsoft.Json

There are still real reasons to reach for it. Pick Newtonsoft.Json when you hit one of these:

The default-leniency difference is the one that bites during a migration. The same payload behaves differently:

// Newtonsoft.Json 13.0.4 - lenient by default, this parses fine
using Newtonsoft.Json;

var config = JsonConvert.DeserializeObject<Config>("""
{
    "Name": "api", // inline comment
    "Retries": 3,
}
""");

public record Config(string Name, int Retries);
// .NET 11, System.Text.Json - strict by default, the same input throws JsonException
using System.Text.Json;

// You must opt in to match Newtonsoft.Json's leniency
var options = new JsonSerializerOptions
{
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true,
    PropertyNameCaseInsensitive = true
};
var config = JsonSerializer.Deserialize<Config>(input, options);

That strictness is the most common source of the post-migration surprise where a payload that worked for years suddenly throws, which is also why errors like a JSON value that could not be converted or a DateTime that fails to parse show up right after a switch.

What the benchmarks actually show

Performance is the easiest claim to hand-wave, so here are concrete numbers rather than the word “faster.” Published BenchmarkDotNet runs on .NET 10 serializing a collection of 10,000 simple POCO objects show System.Text.Json finishing in about 3.7 ms while allocating 3.4 MB, versus Newtonsoft.Json at about 7.6 ms and 8.1 MB for the same workload.

Metric (serialize 10,000 POCOs)System.Text.JsonNewtonsoft.Json 13.x
Mean time~3.7 ms~7.6 ms
Allocated~3.4 MB~8.1 MB
Relative1.0x (baseline)~2.0x slower, ~2.4x memory

Methodology and caveats matter, so read these numbers honestly:

The summary is consistent across every credible benchmark in 2025 and 2026: System.Text.Json is roughly twice as fast and allocates roughly half to a third as much for the common case. The margin has not narrowed in Newtonsoft.Json’s favor.

The gotchas that pick for you

Sometimes one constraint settles the decision before preferences enter the room.

Native AOT is a hard gate. If your deployment target requires AOT (a trimmed container, a scale-to-zero function, an iOS build), Newtonsoft.Json is simply not an option. It depends on runtime reflection that AOT does not provide. This single fact disqualifies it for an entire class of modern .NET workloads.

Maintenance trajectory. Newtonsoft.Json is not dead and not deprecated. James Newton-King, who now works at Microsoft on System.Text.Json itself, still ships security and bug-fix releases (13.0.4 landed 2025-12-30). But it is explicitly in maintenance mode: no major feature work, no AOT story coming. System.Text.Json gets new capabilities every .NET release. Betting new code on the library that is actively evolving is the lower-risk choice for a system you will maintain for years.

Reference handling and object cycles differ. Newtonsoft.Json has ReferenceLoopHandling and PreserveReferencesHandling. System.Text.Json maps these to ReferenceHandler.IgnoreCycles and ReferenceHandler.Preserve, but the behavior is not identical (IgnoreCycles writes null where Newtonsoft.Json drops the property). If you serialize EF Core entity graphs, this is the difference between a clean payload and a possible-object-cycle exception. Know which handler you need before you migrate.

The licenses are identical. Both ship under MIT, so unlike the MediatR or AutoMapper situation, licensing is not a factor here. Do not let “is Newtonsoft.Json still free” drive the decision: it is, and so is System.Text.Json.

The call, in one line

For new .NET 11 code in 2026: default to System.Text.Json, and only add Newtonsoft.Json when you hit a concrete, named feature it cannot do (JSONPath, TypeNameHandling, lenient parsing, or an unsupported type with no converter). It is in-box, faster, AOT-ready, and the one Microsoft is still building. Keep Newtonsoft.Json on existing systems that depend on its flexibility; do not rush a migration that has no payoff, but do not start there either. The strategic default flipped years ago, and in 2026 the gap is wide enough that the burden of proof is on choosing Newtonsoft.Json, not on choosing the in-box library.

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back