1. Introduction
Newtonsoft.Json is kind of a dinosaur in a good way. It was the de-facto standard for working with JSON in the .NET ecosystem for years, long before System.Text.Json even existed. Millions of projects, thousands of libraries and frameworks (even many parts of ASP.NET Core until recently) used Json.NET.
Its advantages are that it:
- Is feature-rich and flexible: Json.NET offers a huge number of settings, attributes and customization points for the serialization/deserialization process. It can do things that System.Text.Json either struggles with or can't do at all.
- Is tolerant of "imperfect" JSON: data from external systems isn't always ideal — Json.NET is often more forgiving and lets you deserialize without extra pain.
- Is backwards compatible: if a project or library depends on Json.NET, knowing how to work with it remains necessary.
Of course, System.Text.Json is faster and built with modern requirements in mind. But Newtonsoft.Json is still a powerful tool, especially when you need specific logic or maximum flexibility.
How to install Newtonsoft.Json?
Since it's a third-party library, add the package via NuGet:
- Open the project.
- Right-click the project in Solution Explorer.
- Choose "Manage NuGet Packages...".
- In the search box type Newtonsoft.Json.
- Select the package and click "Install".
After installation you'll have a new dependency — you can use Json.NET!
Feature comparison: Newtonsoft.Json vs System.Text.Json
| Feature | System.Text.Json | Newtonsoft.Json |
|---|---|---|
| Simple serialization/deserialization | Yes | Yes |
| Support for property attributes | Partially | Full |
| Custom converters | Yes | Yes |
| Working with private fields | No | Yes |
| Working with dynamic structures | Limited | Yes |
| LINQ to JSON (JObject/JArray) | No | Yes |
| Reference Loop Handling | Yes | Yes |
| Support for DataTable, DataSet and complex types | No | Yes |
| Performance | Better | Good |
2. Example of serializing and deserializing a simple object
Let's take a common game structure that evolves across lectures:
public class Player
{
public string Name { get; set; }
public int Health { get; set; }
public bool IsAlive { get; set; }
public List<string> Inventory { get; set; }
public Position Position { get; set; }
}
public class Position
{
public int X { get; set; }
public int Y { get; set; }
}
Now let's save a Player object to JSON and restore it:
using Newtonsoft.Json;
Player player1 = new Player
{
Name = "Aragorn",
Health = 100,
IsAlive = true,
Inventory = new List<string> { "sword", "bow", "healing potion" },
Position = new Position { X = 10, Y = 25 }
};
// Serialize to a JSON string
string json = JsonConvert.SerializeObject(player1, Formatting.Indented);
Console.WriteLine(json);
// Deserialize back to a Player object
Player player2 = JsonConvert.DeserializeObject<Player>(json);
Console.WriteLine($"Name: {player2.Name}, health: {player2.Health}");
Everything? Almost, but that's just the tip of the iceberg. Let's explore why Newtonsoft.Json is more interesting and flexible than other libraries.
3. Formatting, settings and advanced options
When you serialize an object you can get either a compact string or nicely formatted JSON. The behavior is controlled by options and settings.
Example: Different formatting options
// Pretty readable JSON
string prettyJson = JsonConvert.SerializeObject(player1, Formatting.Indented);
// Compact "minified" JSON
string compactJson = JsonConvert.SerializeObject(player1, Formatting.None);
Serialization settings
If needed, pass JsonSerializerSettings for fine control:
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore, // Skip fields with null
DefaultValueHandling = DefaultValueHandling.Ignore, // Skip default-valued fields
Formatting = Formatting.Indented
};
string customJson = JsonConvert.SerializeObject(player1, settings);
You get maximum control over the output format: want to skip empty fields — go ahead. Need to serialize even private properties — configure the contract.
4. Controlling fields and properties with attributes
Newtonsoft.Json supports a powerful attribute system that configures serialization right in the class.
JsonProperty — Renaming properties
If the JSON protocol requires a different field name:
public class Player
{
[JsonProperty("player_name")]
public string Name { get; set; }
// ...
}
Resulting JSON:
{ "player_name": "Aragorn", ... }
JsonIgnore — Ignore a property
public class Player
{
[JsonIgnore]
public int Health { get; set; }
}
Now the Health field will disappear during serialization.
JsonConverter — Custom conversions
Allows specifying which converter should handle a specific field.
public class Player
{
[JsonConverter(typeof(InventoryToStringConverter))]
public List<string> Inventory { get; set; }
}
(More about converters below.)
5. Working with nested objects and collections
Json.NET handles nested objects, arrays, collections and dictionaries very well.
Example: Dictionaries
public class GameStats
{
public Dictionary<string, int> Scores { get; set; }
}
GameStats stats = new GameStats
{
Scores = new Dictionary<string, int>
{
["Alice"] = 1023,
["Bob"] = 999
}
};
string statsJson = JsonConvert.SerializeObject(stats, Formatting.Indented);
Console.WriteLine(statsJson);
The JSON will look like this:
{
"Scores": {
"Alice": 1023,
"Bob": 999
}
}
6. Complex structures: cyclic references, Self-Referencing Objects
Sometimes objects reference each other. Newtonsoft.Json supports serializing such structures via special settings.
Example: Resolving cyclic references
public class Person
{
public string Name { get; set; }
public Person Parent { get; set; }
public List<Person> Children { get; set; }
}
// Configure serialization for cycles
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore // or .Serialize
};
Person p1 = new Person { Name = "Papa" };
Person p2 = new Person { Name = "Son", Parent = p1 };
p1.Children = new List<Person> { p2 };
string json = JsonConvert.SerializeObject(p1, settings);
Console.WriteLine(json);
By default, if you leave ReferenceLoopHandling = Error, you'll get an exception. This protects from accidental infinite serialization.
7. Working with dynamic structures: JObject, JArray
When the JSON structure is unknown in advance or changes on the fly, use dynamic objects without strict C# typing.
Main types:
- JObject — representation of a JSON object.
- JArray — representation of an array.
using Newtonsoft.Json.Linq;
// Convert a string to a JObject
string json = @"{ 'name': 'Aragorn', 'health': 100 }";
JObject obj = JObject.Parse(json);
Console.WriteLine((string)obj["name"]); // Aragorn
Console.WriteLine((int)obj["health"]); // 100
// Dynamically add properties
obj["class"] = "Ranger";
Console.WriteLine(obj.ToString());
Iterating over an array
string jsonArr = @"['apple', 'banana', 'cherry']";
JArray array = JArray.Parse(jsonArr);
foreach (JToken item in array)
{
Console.WriteLine(item);
}
8. Versioning support and required fields
JSON schema can change, fields may be missing. Use attributes and settings:
- [JsonProperty(Required = Required.Always)] — require a field to be present (otherwise an exception).
- [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] — populate default if the field is missing.
public class Player
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue(50)]
public int Health { get; set; }
}
9. Date and time formatting
Working with dates often requires an explicit format.
var dateSettings = new JsonSerializerSettings
{
DateFormatString = "yyyy-MM-dd"
};
string json = JsonConvert.SerializeObject(DateTime.Now, dateSettings);
Console.WriteLine(json); // "2024-06-15"
And back when deserializing:
string dateJson = "\"2024-06-15\""; // Note: this is a quoted string!
DateTime dt = JsonConvert.DeserializeObject<DateTime>(dateJson);
Console.WriteLine(dt);
GO TO FULL VERSION