Skip to content

Json.NET

Serialization & FormatsSerializationC#

What it is

Json.NET (Newtonsoft.Json) is the long-established JSON library for .NET, with extensive customisation, LINQ to JSON and broad format tolerance.

Serialize and deserialize with static helpers, control behaviour with attributes or settings, and query loosely-structured documents with JObject and JToken.

Installation

dotnet add package Newtonsoft.Json

Getting started

The smallest useful thing you can do with it, and what each part means.

Serialisation with settings
var settings = new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    NullValueHandling = NullValueHandling.Ignore,
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    // Never enable TypeNameHandling on untrusted input — it is a
    // remote code execution vector.
    TypeNameHandling = TypeNameHandling.None,
};

var json = JsonConvert.SerializeObject(book, settings);
var back = JsonConvert.DeserializeObject<Book>(json, settings);
TypeNameHandling embeds .NET type names in the payload and instantiates them on read. Deserialising untrusted JSON with it enabled has produced real CVEs — leave it off.
LINQ to JSON for unknown shapes
var doc = JObject.Parse(payload);

string? title = (string?)doc["books"]?[0]?["title"];

// Query across the document without a matching class.
var recent = doc["books"]!
    .Where(b => (int)b["year"]! > 1990)
    .Select(b => (string)b["title"]!)
    .ToList();
This is Json.NET's strongest remaining advantage: navigating a document whose shape you do not know or control, without defining types for it.

Advanced usage

Where the library earns its place over a simpler alternative.

Custom converters
public class MoneyConverter : JsonConverter<Money>
{
    public override void WriteJson(JsonWriter w, Money v, JsonSerializer s) =>
        w.WriteValue($"{v.Amount}:{v.Currency}");

    public override Money ReadJson(JsonReader r, Type t, Money existing,
        bool hasExisting, JsonSerializer s)
    {
        var parts = ((string)r.Value!).Split(':');
        return new Money(decimal.Parse(parts[0]), parts[1]);
    }
}

[JsonConverter(typeof(MoneyConverter))]
public record Money(decimal Amount, string Currency);
Attaching the converter by attribute keeps the mapping with the type, so every serialisation path picks it up without extra configuration.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Unexpected character encountered while parsing
The input is not JSON — often an HTML error page from a failed request. Check the status code before parsing.
Self referencing loop detected
A bidirectional entity relationship. Set ReferenceLoopHandling.Ignore, or serialise a DTO instead of the entity.

Best practices

  • Never enable TypeNameHandling for data from outside your system.
  • Prefer System.Text.Json for new code; it is faster and needs no dependency.
  • Configure settings once and reuse them rather than passing them ad hoc.
  • Use LINQ to JSON when the document shape is unknown; use typed models when it is not.

Background

Why it exists, and what it was reacting to.

Written by James Newton-King, Json.NET was the de facto standard for over a decade. System.Text.Json now ships with .NET and is faster, but Json.NET remains where flexibility matters more than throughput.