How to serialize and deserialize JSON in .NET

Important

The JSON serialization documentation is under construction. This article doesn't cover all scenarios. For more information, examine System.Text.Json issues in the dotnet/corefx repository on GitHub, especially those labeled json-functionality-doc.

This article shows how to use the System.Text.Json namespace to serialize and deserialize to and from JavaScript Object Notation (JSON). The directions and sample code use the library directly, not through a framework such as ASP.NET Core.

Namespaces

The System.Text.Json namespace contains all the entry points and the main types. The System.Text.Json.Serialization namespace contains attributes and APIs for advanced scenarios and customization specific to serialization and deserialization. Therefore, the code examples shown in this article require one or both of the following using directives:

using System.Text.Json;
using System.Text.Json.Serialization;

Attributes from the System.Runtime.Serialization namespace aren't currently supported in System.Text.Json.

How to write .NET objects to JSON (serialize)

To write JSON to a string, call the JsonSerializer.Serialize method. The following example uses an overload with a generic type parameter:

WeatherForecast weatherForecast;
//...
string json = JsonSerializer.Serialize<WeatherForecast>(weatherForecast);

You can omit the generic type parameter and use generic type inference instead:

WeatherForecast weatherForecast;
//...
string json = JsonSerializer.Serialize(weatherForecast);

Here's an example type to be serialized, which contains collections and nested classes:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    public IList<DateTimeOffset> DatesAvailable { get; set;}
    public Dictionary<string, HighLowTemperatures> TemperatureRanges { get; set; }
    public string [] SummaryWords { get; set; }
}

public class HighLowTemperatures
{
    public Temperature High { get; set; }
    public Temperature Low { get; set; }
}

public class Temperature
{
    public int DegreesCelsius { get; set; }
}

The JSON output is minified by default:

{"Date":"2019-08-01T00:00:00-07:00","TemperatureC":25,"Summary":"Hot","DatesAvailable":["2019-08-01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],"TemperatureRanges":{"Cold":{"High":{"DegreesCelsius":20},"Low":{"DegreesCelsius":-10}},"Hot":{"High":{"DegreesCelsius":60},"Low":{"DegreesCelsius":20}}},"SummaryWords":["Cool","Windy","Humid"]}

The following example shows the same JSON, formatted (that is, pretty-printed with whitespace and indentation):

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "TemperatureRanges": {
    "Cold": {
      "High": {
        "DegreesCelsius": 20
      },
      "Low": {
        "DegreesCelsius": -10
      }
    },
    "Hot": {
      "High": {
        "DegreesCelsius": 60
      },
      "Low": {
        "DegreesCelsius": 20
      }
    }
  },
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

Overloads of Serialize let you serialize to a Stream. Async versions of the Stream overloads are available.

Serialize to UTF-8

To serialize to UTF-8, call the JsonSerializer.SerializeToUtf8Bytes method:

byte[] utf8Json = JsonSerializer.SerializeToUtf8Bytes<WeatherForecast>(weatherForecast);

As an alternative, a Serialize overload that takes a Utf8JsonWriter is available.

Serializing to UTF-8 is about 5-10% faster than using the string-based methods. The difference is because the bytes (as UTF-8) don't need to be converted to strings (UTF-16).

Serialization behavior

Supported types include:

How to read JSON into .NET objects (deserialize)

To deserialize from a string, call the JsonSerializer.Deserialize method, as shown in the following example:

string json = ... ;

var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(json);

For an example, see the serialize section. The JSON and .NET object are the same, but the direction is reversed.

Overloads of Deserialize let you deserialize from a Stream. Async versions of the Stream overloads are available.

Deserialize from UTF-8

To deserialize from UTF-8, call a JsonSerializer.Deserialize overload that takes a Utf8JsonReader or a ReadOnlySpan<byte>, as shown in the following examples:

byte[] utf8Json;
//...
var readOnlySpan = new ReadOnlySpan<byte>(utf8Json);
weatherForecast = JsonSerializer.Deserialize<WeatherForecastMin>(readOnlySpan);
byte[] utf8Json;
//...
var utf8Reader = new Utf8JsonReader(utf8Json);
weatherForecast = JsonSerializer.Deserialize<WeatherForecastMin>(ref utf8Reader);

Deserialization behavior

  • By default, property name matching is case-sensitive. You can specify case-insensitivity.
  • If the JSON contains a value for a read-only property, the value is ignored and no exception is thrown.
  • Deserialization to reference types without a parameterless constructor isn't supported.
  • Deserialization to immutable objects or read-only properties isn't supported. For more information, see the GitHub issue on immutable object support and the issue on read-only property support in the dotnet/corefx repository on GitHub.
  • By default, enums are supported as numbers.
  • Fields aren't supported.
  • By default, comments or trailing commas in the JSON throw exceptions. You can allow comments and trailing commas if needed.
  • The default maximum depth is 64.

Serialize to formatted JSON

To pretty-print the JSON output, set JsonSerializerOptions.WriteIndented to true:

var options = new JsonSerializerOptions
{
    WriteIndented = true,
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example type to be serialized and JSON output:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot"
}

Allow comments and trailing commas

By default comments and trailing commas are not allowed in JSON. To allow comments in the JSON, set the JsonSerializerOptions.ReadCommentHandling property to JsonCommentHandling.Skip. And to allow trailing commas, set the JsonSerializerOptions.AllowTrailingCommas property to true. The following example shows how to allow both:

var options = new JsonSerializerOptions
{
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(json, options);

Here's example JSON with comments and a trailing comma:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25, // Fahrenheit 77
  "Summary": "Hot", /* Zharko */
}

Customize JSON names

By default, property names and dictionary keys are unchanged in the JSON output, including case. This section explains how to:

  • Customize individual property names
  • Convert all property names to camel case
  • Implement a custom property naming policy
  • Convert dictionary keys to camel case

Currently, there's no support for automatically converting enums to camel case. For more information, see the issue on enum camel case support in the dotnet/corefx repository on GitHub.

Customize individual property names

To set the name of individual properties, use the [JsonPropertyName] attribute.

Here's an example type to serialize and resulting JSON:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot",
  "Wind": 35
}

The property name set by this attribute:

  • Applies in both directions, for serialization and deserialization.
  • Takes precedence over property naming policies.

Use camel case for all JSON property names

To use camel case for all JSON property names, set JsonSerializerOptions.PropertyNamingPolicy to JsonNamingPolicy.CamelCase, as shown in the following example:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example class to serialize and JSON output:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
{
  "date": "2019-08-01T00:00:00-07:00",
  "temperatureC": 25,
  "summary": "Hot",
  "Wind": 35
}

The camel case property naming policy:

  • Applies to serialization and deserialization.
  • Is overridden by [JsonPropertyName] attributes.

Use a custom JSON property naming policy

To use a custom JSON property naming policy, create a class that derives from JsonNamingPolicy and override the ConvertName method, as shown in the following example:

class UpperCaseNamingPolicy : JsonNamingPolicy
{
    public override string ConvertName(string name)
    {
        return name.ToUpper();
    }
}

Then set the JsonSerializerOptions.PropertyNamingPolicy property to an instance of your naming policy class:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = new UpperCaseNamingPolicy()
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example class to serialize and JSON output:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
{
  "DATE": "2019-08-01T00:00:00-07:00",
  "TEMPERATUREC": 25,
  "SUMMARY": "Hot",
  "Wind": 35
}

The JSON property naming policy:

  • Applies to serialization and deserialization.
  • Is overridden by [JsonPropertyName] attributes.

Camel case dictionary keys

If a property of an object to be serialized is of type Dictionary<string,TValue>, the string keys can be converted to camel case. To do that, set DictionaryKeyPolicy to JsonNamingPolicy.CamelCase, as shown in the following example:

var options = new JsonSerializerOptions
{
    DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example object to serialize and JSON output:

Property Value
Date 8/1/2019 12:00:00 AM -07:00
TemperatureC 25
Summary Hot
TemperatureRanges Cold, 20
Hot, 40
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot",
  "TemperatureRanges": {
    "cold": 20,
    "hot": 40
  }
}

The camel case naming policy applies to serialization only.

Exclude properties

By default, all public properties are serialized. If you don't want some of them to appear in the JSON output, you have several options. This section explains how to exclude:

  • Individual properties
  • All read-only properties
  • All null-value properties

Exclude individual properties

To ignore individual properties, use the [JsonIgnore] attribute.

Here's an example type to serialize and JSON output:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    [JsonIgnore]
    public int WindSpeed { get; set; }
}
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot"
}

Exclude all read-only properties

To exclude all read-only properties, set the JsonSerializerOptions.IgnoreReadOnlyProperties to true, as shown in the following example:

var options = new JsonSerializerOptions
{
    IgnoreReadOnlyProperties = true
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example type to serialize and JSON output:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    public int WindSpeed { get; private set; }
}
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot",
}

This option applies only to serialization. During deserialization, read-only properties are ignored by default. A property is read-only if it contains a public getter but not a public setter.

Exclude all null value properties

To exclude all null value properties, set the IgnoreNullValues property to true, as shown in the following example:

var options = new JsonSerializerOptions
{
    IgnoreNullValues = true
};
json = JsonSerializer.Serialize(weatherForecast, options);

Here's an example object to serialize and JSON output:

Property Value
Date 8/1/2019 12:00:00 AM -07:00
TemperatureC 25
Summary null
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25
}

This setting applies to serialization and deserialization. During deserialization, null values in the JSON are ignored only if they are valid. Null values for non-nullable value types cause exceptions. For more information, see the issue on non-nullable value types in the dotnet/corefx repository on GitHub.

Case-insensitive property matching

By default, deserialization looks for case-sensitive property name matches between JSON and the target object properties. To change that behavior, set the JsonSerializerOptions.PropertyNameCaseInsensitive to true:

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true,
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(weatherForecast, options);

Here's example JSON with camel case property names. It can be deserialized into the following type that has Pascal case property names.

{
  "date": "2019-08-01T00:00:00-07:00",
  "temperatureC": 25,
  "summary": "Hot",
}
class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}

Include properties of derived classes

Polymorphic serialization isn't supported when you specify at compile time the type to be serialized. For example, suppose you have a WeatherForecast class and a derived class WeatherForecastWithWind:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}
class WeatherForecastWithWind : WeatherForecast
{
    public int WindSpeed { get; set; }
}

And suppose the type passed to, or inferred by, the Serialize method at compile time is WeatherForecast:

string json = JsonSerializer.Serialize<WeatherForecast>(weatherForecast);
WeatherForecast weatherForecast;
//...
json = JsonSerializer.Serialize(weatherForecast);

In this scenario, the WindSpeed property is not serialized even if the weatherForecast object is actually a WeatherForecastWithWind object. Only the base class properties are serialized:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot"
}

This behavior is intended to help prevent accidental exposure of data in a derived runtime-created type.

To serialize the properties of the derived type, use one of the following approaches:

  • Call an overload of Serialize that lets you specify the type at runtime:

    json = JsonSerializer.Serialize(weatherForecast, weatherForecast.GetType());
    
  • Declare the object to be serialized as object.

    json = JsonSerializer.Serialize<object>(weatherForecast);
    

In the preceding example scenario, both approaches cause the WindSpeed property to be included in the JSON output:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 25,
  "Summary": "Hot",
  "WindSpeed": 35
}

Handle overflow JSON

While deserializing, you might receive data in the JSON that is not represented by properties of the target type. For example, suppose your target type is this:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
}

And the JSON to be deserialized is this:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "temperatureC": 25,
  "Summary": "Hot",
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

If you deserialize the JSON shown into the type shown, the DatesAvailable and SummaryWords properties have nowhere to go and are lost. To capture extra data such as these properties, apply the JsonExtensionData attribute to a property of type Dictionary<string,object> or Dictionary<string,JsonElement>:

class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureC { get; set; }
    public string Summary { get; set; }
    [JsonExtensionData]
    public Dictionary<string, object> ExtensionData { get; set; }
}

When you deserialize the JSON shown earlier into this sample type, the extra data becomes key-value pairs of the ExtensionData property:

Property Value Notes
Date 8/1/2019 12:00:00 AM -07:00
TemperatureC 0 Case-sensitive mismatch (temperatureC in the JSON), so the property isn't set.
Summary Hot
ExtensionData temperatureC: 25 Since the case didn't match, this JSON property is an extra and becomes a key-value pair in the dictionary.
DatesAvailable:
8/1/2019 12:00:00 AM -07:00
8/2/2019 12:00:00 AM -07:00
Extra property from the JSON becomes a key-value pair, with an array as the value object.
SummaryWords:
Cool
Windy
Humid
Extra property from the JSON becomes a key-value pair, with an array as the value object.

When the target object is serialized, the extension data key value pairs become JSON properties just as they were in the incoming JSON:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureC": 0,
  "Summary": "Hot",
  "temperatureC": 25,
  "DatesAvailable": [
    "2019-08-01T00:00:00-07:00",
    "2019-08-02T00:00:00-07:00"
  ],
  "SummaryWords": [
    "Cool",
    "Windy",
    "Humid"
  ]
}

Notice that the ExtensionData property name doesn't appear in the JSON. This behavior lets the JSON make a round trip without losing any extra data that otherwise wouldn't be deserialized.

Use Utf8JsonWriter directly

The following example shows how to use the Utf8JsonWriter class directly.

var options = new JsonWriterOptions
{
    Indented = true
};

using (var stream = new MemoryStream())
{
    using (var writer = new Utf8JsonWriter(stream, options))
    {
        writer.WriteStartObject();
        writer.WriteString("date", DateTimeOffset.UtcNow);
        writer.WriteNumber("temp", 42);
        writer.WriteEndObject();
    }

    string json = Encoding.UTF8.GetString(stream.ToArray());
    Console.WriteLine(json);
}

Use Utf8JsonReader directly

The following example shows how to use the Utf8JsonReader class directly. The code assumes that the jsonUtf8 variable is a byte array that contains valid JSON, encoded as UTF-8.

var options = new JsonReaderOptions
{
    AllowTrailingCommas = true,
    CommentHandling = JsonCommentHandling.Skip
};
Utf8JsonReader reader = new Utf8JsonReader(jsonUtf8, options);

while (reader.Read())
{
    Console.Write(reader.TokenType);

    switch (reader.TokenType)
    {
        case JsonTokenType.PropertyName:
        case JsonTokenType.String:
            {
                string text = reader.GetString();
                Console.Write(" ");
                Console.Write(text);
                break;
            }

        case JsonTokenType.Number:
            {
                int value = reader.GetInt32();
                Console.Write(" ");
                Console.Write(value);
                break;
            }

            // Other token types elided for brevity
    }

    Console.WriteLine();
}

Additional resources