Parse JSON in C#

C# offers two mature paths for parsing JSON: System.Text.Json, the built-in library shipped with .NET Core 3.0 and .NET 5+, and Newtonsoft.Json (Json.NET), the long-standing community favorite with the richest feature set. Both convert a JSON string into strongly-typed C# objects and back again in just a few lines of code.

Validate your JSON in Jsonic before deserializing it in C#.

Open JSON Formatter

Two options: System.Text.Json vs Newtonsoft.Json

Choosing between the two libraries comes down to your target framework and feature requirements. System.Text.Json is included in every .NET 5+ project with no extra package — it is faster and allocates less memory. Newtonsoft.Json has a decade of ecosystem support, supports .NET Framework 4.x, and provides capabilities like LINQ-to-JSON and more flexible custom converters that System.Text.Json still lacks.

FeatureSystem.Text.JsonNewtonsoft.Json
Ships with .NETYes (.NET Core 3.0+, .NET 5+)No (NuGet package)
.NET Framework supportPartial (via NuGet)Full (.NET 4.0+)
PerformanceFaster, lower allocationSlightly slower
Dynamic DOM (read-only)JsonDocumentJObject / JToken
Dynamic DOM (mutable)JsonNode (.NET 6+)JObject
LINQ-to-JSONNoYes (JToken / SelectToken)
Field rename annotation[JsonPropertyName][JsonProperty]
Recommended forNew .NET 5+ projects.NET Framework or complex scenarios

System.Text.Json: basic deserialization

System.Text.Json is available in the System.Text.Json namespace — no NuGet package required for modern .NET projects. Define a POCO (Plain Old CLR Object) with public properties, then call JsonSerializer.Deserialize<T>(json):

using System.Text.Json;

// Define your C# class (properties must match JSON keys)
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public decimal Price { get; set; }
    public bool InStock { get; set; }
}

string json = """
{
  "id": 42,
  "name": "Widget Pro",
  "price": 9.99,
  "inStock": true
}
""";

// Deserialize JSON string to C# object
Product product = JsonSerializer.Deserialize<Product>(json)!;
Console.WriteLine(product.Name);  // Widget Pro
Console.WriteLine(product.Price); // 9.99

// Handle camelCase JSON → PascalCase C# automatically
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PropertyNameCaseInsensitive = true,
};
Product p2 = JsonSerializer.Deserialize<Product>(json, options)!;

// Deserialize an array
string arrayJson = """[{"id":1,"name":"A"},{"id":2,"name":"B"}]""";
List<Product> products = JsonSerializer.Deserialize<List<Product>>(arrayJson)!;

// Serialize back to JSON
string output = JsonSerializer.Serialize(product, new JsonSerializerOptions { WriteIndented = true });

By default, System.Text.Json performs case-sensitive property matching. Pass a JsonSerializerOptions with PropertyNameCaseInsensitive = true to accept any casing, or set PropertyNamingPolicy = JsonNamingPolicy.CamelCase to automatically map camelCase JSON keys to PascalCase C# properties.

JsonDocument for dynamic JSON (read-only DOM)

When you don't have a POCO that matches the JSON shape — or when you only need to read a few fields from a large payload — use JsonDocument. It parses JSON into a low-allocation, read-only DOM. Always wrap it in a using statement so the internal buffer is returned to the pool when you're done:

using System.Text.Json;

string json = """{"user":{"name":"Alice","age":30},"tags":["admin","user"]}""";

using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;

// Read a nested string
string name = root.GetProperty("user").GetProperty("name").GetString()!;
Console.WriteLine(name); // Alice

// Read a number
int age = root.GetProperty("user").GetProperty("age").GetInt32();

// Iterate an array
foreach (JsonElement tag in root.GetProperty("tags").EnumerateArray())
{
    Console.WriteLine(tag.GetString());
}

// Check if a property exists
if (root.TryGetProperty("email", out JsonElement email))
{
    Console.WriteLine(email.GetString());
}

TryGetProperty is the safe way to read optional keys — it returns false instead of throwing KeyNotFoundException when a property is absent. GetProperty throws if the key is missing, so only use it for required fields.

JsonNode for mutable JSON (.NET 6+)

JsonNode, added in .NET 6, provides a mutable JSON DOM. Unlike JsonDocument, you can modify values, add properties, and build new JSON objects from scratch without defining POCOs:

using System.Text.Json.Nodes;

string json = """{"name":"Alice","scores":[95,87,92]}""";

// Parse to mutable node tree
JsonNode root = JsonNode.Parse(json)!;

// Read values
string name = root["name"]!.GetValue<string>();
int firstScore = root["scores"]![0]!.GetValue<int>();

// Modify values
root["name"] = "Bob";
root["scores"]!.AsArray().Add(100);

// Build a new JSON object programmatically
var newObj = new JsonObject
{
    ["id"] = 42,
    ["active"] = true,
    ["tags"] = new JsonArray("admin", "user"),
};
Console.WriteLine(newObj.ToJsonString());
// {"id":42,"active":true,"tags":["admin","user"]}

Use JsonNode when you need to construct or transform JSON at runtime without a strongly-typed class. For read-only inspection of large payloads, prefer JsonDocument for its lower memory overhead.

Newtonsoft.Json: JsonConvert.DeserializeObject

Install Newtonsoft.Json via the .NET CLI or NuGet Package Manager:

dotnet add package Newtonsoft.Json

Then use JsonConvert.DeserializeObject<T>() to map JSON to a C# class. Newtonsoft.Json reads and writes public properties and fields directly, and maps camelCase JSON to PascalCase C# properties by default:

// Install: dotnet add package Newtonsoft.Json
using Newtonsoft.Json;

public class User
{
    public string Name { get; set; } = "";
    public int Age { get; set; }

    // Map a different JSON key name
    [JsonProperty("email_address")]
    public string Email { get; set; } = "";
}

string json = """{"name":"Alice","age":30,"email_address":"alice@example.com"}""";

// Deserialize
User user = JsonConvert.DeserializeObject<User>(json)!;
Console.WriteLine(user.Email); // alice@example.com

// Deserialize array
string arrayJson = "[1, 2, 3]";
List<int> numbers = JsonConvert.DeserializeObject<List<int>>(arrayJson)!;

// Serialize with indentation
string output = JsonConvert.SerializeObject(user, Formatting.Indented);

Newtonsoft.Json performs case-insensitive property matching by default — a common source of confusion when switching to System.Text.Json, which is case-sensitive. The [JsonProperty("key")] attribute controls the exact JSON key name for both serialization and deserialization.

JObject for dynamic JSON (Newtonsoft.Json)

Newtonsoft.Json's JObject is a mutable, dictionary-like DOM for working with JSON when you don't have a matching POCO. It supports JSONPath queries via SelectToken for navigating deeply nested structures:

using Newtonsoft.Json.Linq;

string json = """{"product":{"id":1,"tags":["sale","new"]}}""";

// Parse to JObject (dynamic DOM)
JObject root = JObject.Parse(json);

// Navigate with indexer
string? productName = (string?)root["product"]?["id"];

// Navigate with SelectToken (JSONPath)
JToken? firstTag = root.SelectToken("product.tags[0]");
Console.WriteLine(firstTag?.ToString()); // sale

// Modify in place
root["product"]!["price"] = 19.99;

// Create from scratch
var obj = new JObject
{
    ["id"] = 42,
    ["tags"] = new JArray("admin", "user"),
};

SelectToken accepts JSONPath expressions like "product.tags[0]" or "store..price" (recursive descent), making it convenient for deeply nested navigation. Use SelectTokens (plural) to return multiple matching nodes as an IEnumerable<JToken>.

Common JSON attributes in C#

Both libraries provide attributes that control how C# properties are mapped to JSON keys during serialization and deserialization. The attribute names differ between the two libraries, so pay attention to which namespace you're importing:

AttributeLibraryPurposeExample
[JsonPropertyName("key")]System.Text.JsonMap to a different JSON key[JsonPropertyName("first_name")]
[JsonIgnore]System.Text.JsonExclude field from JSON[JsonIgnore]
[JsonRequired]System.Text.JsonMark field as required[JsonRequired]
[JsonProperty("key")]Newtonsoft.JsonMap to a different JSON key[JsonProperty("email_address")]
[JsonIgnore]Newtonsoft.JsonExclude field[JsonIgnore]
[JsonConverter(typeof(T))]BothUse custom converter[JsonConverter(typeof(DateOnlyConverter))]

Note that both libraries define a [JsonIgnore] attribute — if you have both namespaces imported, qualify it as System.Text.Json.Serialization.JsonIgnoreAttribute or Newtonsoft.Json.JsonIgnoreAttribute to avoid ambiguity.

Error handling when parsing JSON in C#

Both libraries throw specific exceptions when parsing fails. Wrap your deserialization call in a try/catch to handle malformed input gracefully:

using System.Text.Json;

// System.Text.Json error handling
try
{
    var product = JsonSerializer.Deserialize<Product>(json);
}
catch (JsonException ex)
{
    Console.WriteLine($"JSON parse error at path {ex.Path}: {ex.Message}");
}

// Newtonsoft.Json error handling
using Newtonsoft.Json;

try
{
    var user = JsonConvert.DeserializeObject<User>(json);
}
catch (JsonReaderException ex)
{
    Console.WriteLine($"Invalid JSON at line {ex.LineNumber}: {ex.Message}");
}
catch (JsonSerializationException ex)
{
    // Thrown when the JSON structure doesn't match the target type
    Console.WriteLine($"Serialization error: {ex.Message}");
}

System.Text.Json throws JsonException for all parse and mapping errors. Newtonsoft.Json distinguishes between JsonReaderException (bad JSON syntax) and JsonSerializationException (valid JSON that doesn't match the target type), letting you handle each case separately.

Frequently asked questions

Should I use System.Text.Json or Newtonsoft.Json?

System.Text.Json is the built-in choice for .NET Core 3.0+ and .NET 5+: zero extra dependencies, better performance, and maintained by Microsoft. Newtonsoft.Json (Json.NET) has broader feature coverage — including LINQ-to-JSON, custom converters, and support for more edge cases — and is required when targeting .NET Framework. For new projects on modern .NET, start with System.Text.Json and switch to Newtonsoft only if you need a specific missing feature.

Why are my C# properties not being deserialized?

System.Text.Json by default matches JSON property names case-sensitively and expects camelCase JSON to map to PascalCase C# properties only when you add [JsonPropertyName] or set JsonNamingPolicy.CamelCase on JsonSerializerOptions. Check that your property is public with a public getter and setter. With Newtonsoft.Json, enable CamelCasePropertyNamesContractResolver or use [JsonProperty("name")] to control the mapping.

How do I deserialize a JSON array in C#?

Pass a list type as the type parameter: var list = JsonSerializer.Deserialize<List<Product>>(json) with System.Text.Json, or var list = JsonConvert.DeserializeObject<List<Product>>(json) with Newtonsoft.Json. Both return a strongly-typed List<T> you can iterate, filter, and sort.

How do I handle nullable JSON fields in C#?

Declare the property as a nullable type: public string? Name {get; set;} or public int? Age {get; set;}. Both System.Text.Json and Newtonsoft.Json set null JSON fields to null in the C# object. To ignore null values during serialization, set DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull in JsonSerializerOptions.

What is the difference between JsonDocument and JsonNode in System.Text.Json?

JsonDocument provides a read-only, low-allocation DOM for inspecting JSON without defining types. JsonNode (added in .NET 6) provides a mutable DOM — you can build and edit JSON trees dynamically. Use JsonDocument for parsing and reading; use JsonNode when you need to construct or modify JSON programmatically without a POCO.

How do I serialize a C# object back to JSON?

With System.Text.Json: string json = JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true }); With Newtonsoft.Json: string json = JsonConvert.SerializeObject(obj, Formatting.Indented); Both accept an optional options/settings parameter to control indentation, null handling, and naming conventions.

Format your JSON

Paste your JSON into Jsonic to validate and format it before deserializing in C#.

Open JSON Formatter