Tuple serialization with .Net Core API

Viewed 1330

I can't serialize tuples. I create a template VS2019 .Net Core API project and replace the controller with:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public List<(int number, string text)> Get()
    {
        var x = new List<(int number, string text)>
        {
            (1, "one"),
            (2, "two"),
            (3, "three")
        };
        return x;
    }

    /*[HttpGet]
    public List<string> Get()
    {
        var x = new List<string>
        {
            "one",
            "two",
            "three"
        };
        return x;
    }*/
}

When called, the first method will return: [{},{},{}] and the second (when uncommented): ["one","two","three"]

Why aren't the tuples serialized? The example is easy to replroduce.

4 Answers

Anonymous objects serialize better than value tuples, but declaring them is more verbose:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public IList<object> Get()
    {
        var x = new List<object>
        {
            new {number = 1, text = "one"},
            new {number = 2, text = "two"},
            new {number = 3, text = "three"}
        };
        return x;
    }
}

I think this makes them clearer and more importantly it returns the expected: [{"number":1,"text":"one"},{"number":2,"text":"two"},{"number":3,"text":"three"}]

If you want to be really clear what your API methods are returning then I would declare DTO/model ojects to return.

I think most serialization libraries use public properties for output generation, and C# 7 tuples are using public fields instead. We can check this by doing some reflection. You can see that if you return object of your own class with public fields only, json response will have the same output. If it does not match your expectations, you could use different serialization strategy. Some handy answers are here

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public object Get()
        {
            var x = (1, 1);

            var properties = x.GetType().GetProperties(); // count == 0
            var fields = x.GetType().GetFields(); // count == 2

            return x;
        }

        [HttpGet("obj")]
        public object GetMyObj()
        {
            return new MyObj();
        }

        public class MyObj
        {
            public int i = 1;
        }
    }
}

You need use Newtonsoft.Json to serialize. Install the package from Nuget:

Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson -Version 3.1.9

And use the below code to serilize it:

var y = JsonConvert.SerializeObject(x);

You can return an OK response:

[HttpGet]
public IHttpActionResult Get()
{
    var x = new List<(int number, string text)>
    {
        (1, "one"),
        (2, "two"),
        (3, "three")
    };
    return Ok(x);
}
Related