Serialization the tuples in Blazor Client

Hi.

I try to use tuples in my request DTO but it does not work correctly. The DTO property field is defined as List<(int element, string some)> is not well json serialised. More details in my next comment with failing test case.

Hi @diskman , screenshots are hard to work with, can you show me some of the code you are working with in codeblocks (```).

I’m not sure what you mean here, show what you are expecting, and what is null that is unexpected. Since you said this is in a Blazor Client, I’m assuming this is Blazor WASM client using the JsonApi client or a different client? Is this serializing between a ServiceStack service or something else?

I appreciate it is frustrating to not have something working, but we need to be able to accurately reproduce the issue to help, otherwise we are just making assumptions that can be wrong, adding to confusion and wasting time. The more details for me to reproduce the issue, the better, remember, I don’t have the same context of your project that you do.

@layoric no problem, please take a look on the code below.

using NUnit.Framework;
using ServiceStack;

namespace APNext.Tests.Infrastructure.TestCases
{
    public class JsonApiClientTests
    {
        const string BaseUri = "https://localhost:2000/";
        ServiceStackHost appHost;

        public JsonApiClientTests()
        {
            appHost = new AppHostTest()
                .Init()
                .Start(BaseUri);
        }

        [OneTimeTearDown]
        public void OneTimeTearDown() => appHost.Dispose();

        public IServiceClient CreateClient() => new JsonApiClient(BaseUri);

        [Test]
        public async Task TestRequestWithTupes_Success()
        {
            var client = CreateClient();

            TestDtoWithTuple req = new TestDtoWithTuple()
            {
                Id = 1,
                ListOfTuples = new List<(int Random, string Key)>()
            };

            req.ListOfTuples.Add((100, "test"));

            await client.PatchAsync(req);
        }
    }

    public class AppHostTest : AppSelfHostBase
    {
        public AppHostTest() : base("Customer REST Example", typeof(TestService).Assembly) { }

        public override void Configure(Container container)
        {
            RegisterService<TestService>();
        }
    }

    public class TestService : Service
    {
        public async Task Patch(TestDtoWithTuple req)
        {
            req.ThrowIfNull();
            req.ListOfTuples.ThrowIfNull();
        }
    }

    [Route("/test", Verbs = "PATCH")]
    public class TestDtoWithTuple : IReturnVoid
    {
        public int Id { get; set; }
        public List<(int Random, string Key)> ListOfTuples { get; set; }
    }
}

It’s because you’re using Value Tuples which are Value Types which ServiceStack Serializers treat as a single scalar type, and Value Tuples don’t support deserializing back from their ToString representation. Basically treating value types as complex types aren’t supported.

If you’re trying to save on boiler plate I’d recommend using records instead, e.g:

public record RandomKey(int Random, string Key);

[Route("/test", Verbs = "PATCH")]
public class TestDtoWithTuple : IReturnVoid
{
    public int Id { get; set; }
    public List<RandomKey> ListOfTuples { get; set; }
}

That can also be populated with the same minimal boilerplate as value tuples:

var dto = new TestDtoWithTuple {
    ListOfTuples = new()
    {
        new(Id:1, Name:"foo"),
        new(Id:2, Name:"Bar"),
    }
};
1 Like

thanks @mythz
Are there any plans to add Value Tuples deserialisation support?

No plans since they won’t be able to be supported in other languages which don’t have the feature, so we don’t want to encourage their use in DTOs.