60

I am curious about how the Tuple<T1, T2, T3, ...> serializes and deserializes. I searched using keywords "json" and "tuple" but I could not find what I want.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118

4 Answers4

56

I test by UnitTest and Json.net, and the test codes is as following. The results shows Tuple<T1,T2,T3,...> is serializable and deserializable. So I can use them in my application.

Test codes

public class Foo {
    public List<Tuple<string, string, bool>> Items { get; set; }

    public Foo()
    {
        Items = new List<Tuple<string, string, bool>>();
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (var a in Items)
        {
            sb.Append(a.Item1 + ", " + a.Item2 + ", " + a.Item3.ToString() + "\r\n");
        }
        return sb.ToString();
    }
}

[TestClass]
public class NormalTests
{
    [TestMethod]
    public void TupleSerialization()
    {
        Foo tests = new Foo();
        
        tests.Items.Add(Tuple.Create("one", "hehe", true));
        tests.Items.Add(Tuple.Create("two", "hoho", false));
        tests.Items.Add(Tuple.Create("three", "ohoh", true));

        string json = JsonConvert.SerializeObject(tests);
        Console.WriteLine(json);

        var obj = JsonConvert.DeserializeObject<Foo>(json);
        string objStr = obj.ToString();
        Console.WriteLine(objStr);
    }
}

Summary

  • Tuple.Create("own","hehe",true) serializes to {"Item1":"one","Item2":"hehe","Item3":true}

  • {"Item1":"one","Item2":"hehe","Item3":true} can be deserialized back to Tuple<string,string, bool>

  • Class Foo with Tuple data, can be serialized to json string, and the string can be deserialized back to Class Foo.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118
  • 1
    Funny thing: it'd seem to fail for types inheriting Tuple – eglasius Mar 31 '16 at 12:57
  • Another funny thing: You need to write your own [JsonConverter](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm) in order to deal with inheritance in some cases. – Anastasios Moraitis Apr 30 '20 at 07:42
  • Another funny thing: What if you try with Tuple being T1 and/or T2 non-serializable? You can only assume it is serializable if is a tuple of serializable types. – Windgate Feb 28 '22 at 09:39
34

If you are looking for a short answer. I am using JsonConvert.

var testTuple = Tuple.Create(1234, "foo", true);
var serialized = JsonConvert.SerializeObject(testTuple);

Console.WriteLine(serialized);
// prints: {"Item1":1234,"Item2":"foo","Item3":true}

I made a minimal fiddle.

Hinrich
  • 13,485
  • 7
  • 43
  • 66
  • 2
    I honestly think that this answer is more helpful than the top-voted answer. It's much clearer and direct to say that a `Tuple` serializes to `{Item1: string, Item2: string}`, than what the top-voted answerer has as their answer. – Andrew Gray Feb 24 '20 at 20:21
  • 5
    I suspect it's because the OP's answer had five years to gather upvotes before this answer :) – Jag Nov 04 '20 at 11:07
10

With .NET5 and soon .NET6 it's now recommended to use System.Text.Json over NewtonSoft. The important thing for this serializer with regard to tuples is to set the JsonSerializerOptions option IncludeFields, as otherwise tuple values are excluded by default.

Further, named tuples are just syntactic sugar which are replaced by standard Item1, Item2 notation by the compiler. To include names the simplest way is to use an anonymous object.

Below is a minimal example. (can paste into .NET fiddle with the .NET5 compiler)


using System;
using System.Collections.Generic;
using System.Text.Json;
                    
public class Program
{
    public static void Main()
    {
        JsonSerializerOptions options = new() { IncludeFields = true };

        var testTuple = ("test" , "test1", 1324, false);
        var serializedTuple = JsonSerializer.Serialize(testTuple, options);
        Console.WriteLine(serializedTuple);
        
        var testTuple2 = (NamedItem1: "test" , NamedItemTwo: "test1", TheIntegersName: 1324, ThisBoolHasAFirstNameIts: false);
        var serializedTuple2 = JsonSerializer.Serialize(new {testTuple2.NamedItem1, testTuple2.NamedItemTwo, testTuple2.TheIntegersName, testTuple2.ThisBoolHasAFirstNameIts }, options);
        Console.WriteLine(serializedTuple2);
        
    }
}

output:

{"Item1":"test","Item2":"test1","Item3":1324,"Item4":false}

{"NamedItem1":"test","NamedItemTwo":"test1","TheIntegersName":1324,"ThisBoolHasAFirstNameIts":false}

TheAtomicOption
  • 1,456
  • 1
  • 12
  • 21
  • 2
    Is there a way to change the naming of the tuple items from `ItemN` to the custom name in the code? For example: `(int x, int y) point` should output `"point": { "x": 3, "y": 6 }` instead of `"point": { "Item1": 3, "Item2": 6 }`. – cvbattum Nov 07 '21 at 12:35
  • It's only sort of possible because named tuples are just syntactic sugar so the names aren't retained at compile time for use at runtime (see this answer to a similar question: https://stackoverflow.com/a/57217505/3626160). The simplest workaround is to put the named tuple values into an anonymous object first, as in this answer (https://stackoverflow.com/a/66386667/3626160). I'll update my answer to include this example. – TheAtomicOption Nov 08 '21 at 16:33
1

Thank you Hinrich to the dotnetfiddle link above.

i used the same link, and got to know how Conversion works between Json objects and Tuples. Below is the code :

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class Program
{
    public static void Main()
    {

        var testTuple = Tuple.Create<int, string, bool>(1234, "foo", true);
        var serialized = JsonConvert.SerializeObject(testTuple);
        Console.WriteLine(serialized);
        JObject test = ((JObject)JsonConvert.DeserializeObject(serialized));
        string strSerialized = JsonConvert.SerializeObject(test);
        //Tuple<int, string, bool> testTuple1 = JsonConvert.DeserializeObject<Tuple<int, string, bool>>(serialized); // WORKs
        Tuple<int, string, bool> testTuple1 = JsonConvert.DeserializeObject<Tuple<int, string, bool>>(strSerialized); // WORKs
        Console.WriteLine(testTuple1.Item1.ToString());
    }
}

Hope someone finds this helpful.