C# – Serialize a tuple to JSON

When you serialize a tuple to JSON, it uses the the underlying ValueTuple’s field names – Item1 and Item2. It doesn’t matter if you’re using a named tuple, it won’t use the names you specified in the tuple declaration. This behavior is the same with Newtonsoft and System.Text.Json.

Here’s an example of serializing a named and unnamed tuple with Newtonsoft:

using Newtonsoft.Json;

var namedTuple = (name: "Seneca", philosophy: "Stoic");
var namedTupleJSON = JsonConvert.SerializeObject(namedTuple);

Console.WriteLine("Named Tuple JSON:");
Console.WriteLine(namedTupleJSON);

var unnamedTuple = ("Seneca", "Stoic");
var unamedTupleJSON = JsonConvert.SerializeObject(unnamedTuple);

Console.WriteLine("Unnamed Tuple JSON:");
Console.WriteLine(unamedTupleJSON);
Code language: C# (cs)

Here’s what this outputs:

Named Tuple JSON:
{"Item1":"Seneca","Item2":"Stoic"}

Unnamed Tuple JSON:
{"Item1":"Seneca","Item2":"Stoic"}Code language: plaintext (plaintext)

Notice the JSON is the same whether you’re using a named or unnamed tuple. The reason is because tuples are just syntax sugar for using ValueTuple<T1, T2>. The underlying type in this example is ValueTuple<string, string>, which has two fields: string Item1 and string Item2. Hence why the serializer outputs JSON with Item1 and Item2.

Deserializing to a tuple works the same way.

  • The JSON must have properties with the ValueTuple field names – Item1 and Item2.
  • Specify the deserialization type like this DeserializeObject<(string, string)>.
  • Using named or unnamed tuples makes no difference.

Here’s an example:

using Newtonsoft.Json;

(string name, string philosophy) person = JsonConvert.DeserializeObject<(string, string)>(json);
Console.WriteLine(person.name); //outputs Seneca
Code language: C# (cs)

It deserializes the tuple fields by position. So it maps Item1 to the first property (string name) and Item2 to the second property (string philosophy). Naming the tuple fields has no effect whatsoever.

Use a different type if you want named JSON properties

Named tuples don’t really have properties with the names you specified. It’s just compiler magic that makes tuples easier for devs to use. Unfortunately, this leads to surprises when you go to serialize the tuple. The JSON is using the real field names – Item1 and Item2 – instead of the “nice for developer” names that you thought you were using.

If that’s a problem, you’ll want to use something other than a tuple. For example, you could load the tuple into a dictionary or anonymous type. Named tuples are suppose to be a time saver (because you don’t have to create a class). If they cease to save you time, I suggest creating a class instead.

Serialize a tuple with System.Text.Json

Tuples are syntax sugar for ValueTuple<T1, T2>, which has fields (not properties) called Item1 and Item2. By default, System.Text.Json ignores fields by default, hence it totally ignores tuples.

To serialize a tuple with System.Text.Json, you have to use setting JsonSerializerOptions.IncludeFields (added in .NET 5). Here’s an example of using this to serialize a named and unnamed tuple:

using System.Text.Json;

var namedTuple = (name: "Seneca", philosophy: "Stoic");
var namedTupleJSON = JsonSerializer.Serialize(namedTuple, new JsonSerializerOptions()
{
    IncludeFields = true
});

Console.WriteLine("Named Tuple JSON:");
Console.WriteLine(namedTupleJSON);

var unnamedTuple = ("Seneca", "Stoic");
var unamedTupleJSON = JsonSerializer.Serialize(unnamedTuple, new JsonSerializerOptions()
{
    IncludeFields = true
});

Console.WriteLine("Unnamed Tuple JSON:");
Console.WriteLine(unamedTupleJSON);
Code language: C# (cs)

This outputs the following:

Named Tuple JSON:
{"Item1":"Seneca","Item2":"Stoic"}

Unnamed Tuple JSON:
{"Item1":"Seneca","Item2":"Stoic"}Code language: plaintext (plaintext)

Notice they are exact same. Using a named tuple makes no difference.

Deserialization works the same.

  • The JSON has to have fields Item1 and Item2.
  • You have to use JsonSerializerOptions.IncludeFields.
  • Specify the deserialization type like this: Deserialize<(string, string)>.
  • Using an unnamed or named tuple makes no difference.

Here’s an example:

using System.Text.Json;

var jsonOptions = new JsonSerializerOptions()
{
    IncludeFields = true
};

(string name, string philosophy) namedTuple = JsonSerializer.Deserialize<(string, string)>(json, jsonOptions);
Console.WriteLine(namedTuple.name); //outputs Seneca

Code language: C# (cs)