Skip to content

Commit

Permalink
Add special serialization handling for nullable ValueTuples (#4713)
Browse files Browse the repository at this point in the history
This commit adds special serialization handling for nullable ValueTuples.
Specialized handling is required as without, a nullable ValueTuple type
ends up falling into CustomDyanmicObjectResolver inside of
NestFormatterResolver, which will build a dynamic formatter using
MetaType et.al, which enumerates interfaces and can cause a
CLR error when calling `at System.Runtime.CompilerServices.ITuple.get_Length()`.

Fixes #4703
  • Loading branch information
russcam authored May 27, 2020
1 parent f3b329f commit 55cc44c
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/Elasticsearch.Net/Utf8Json/Resolvers/DynamicGenericResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,45 @@ internal static object GetFormatter(Type t)

return CreateInstance(tupleFormatterType, ti.GenericTypeArguments);
}

// Nullable ValueTuple
else if (isNullable && nullableElementType.GetTypeInfo().IsConstructedGenericType() &&
nullableElementType.GetTypeInfo().FullName.StartsWith("System.ValueTuple"))
{
Type tupleFormatterType = null;
switch (nullableElementType.GenericTypeArguments.Length)
{
case 1:
tupleFormatterType = typeof(ValueTupleFormatter<>);
break;
case 2:
tupleFormatterType = typeof(ValueTupleFormatter<,>);
break;
case 3:
tupleFormatterType = typeof(ValueTupleFormatter<,,>);
break;
case 4:
tupleFormatterType = typeof(ValueTupleFormatter<,,,>);
break;
case 5:
tupleFormatterType = typeof(ValueTupleFormatter<,,,,>);
break;
case 6:
tupleFormatterType = typeof(ValueTupleFormatter<,,,,,>);
break;
case 7:
tupleFormatterType = typeof(ValueTupleFormatter<,,,,,,>);
break;
case 8:
tupleFormatterType = typeof(ValueTupleFormatter<,,,,,,,>);
break;
default:
break;
}

var tupleFormatter = CreateInstance(tupleFormatterType, nullableElementType.GenericTypeArguments);
return CreateInstance(typeof(StaticNullableFormatter<>), new [] { nullableElementType }, tupleFormatter);
}
#endif

// ArraySegement
Expand Down
38 changes: 38 additions & 0 deletions tests/Tests.Reproduce/GithubIssue4703.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information

using System;
using System.Text;
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
using FluentAssertions;
using Nest;
using Tests.Core.Client;

namespace Tests.Reproduce
{
public class GithubIssue4703
{
[U]
public void NullableValueTupleDoesNotThrow()
{
Func<IndexResponse> action = () =>
TestClient.DefaultInMemoryClient.Index(
new ExampleDoc
{
tupleNullable = ("somestring", 42),
}, i => i.Index("index"));

var a = action.Should().NotThrow();
var response = a.Subject;

var json = Encoding.UTF8.GetString(response.ApiCall.RequestBodyInBytes);
json.Should().Be(@"{""tupleNullable"":{""Item1"":""somestring"",""Item2"":42}}");
}
}

public class ExampleDoc
{
public (string info, int number)? tupleNullable { get; set; }
}
}

0 comments on commit 55cc44c

Please sign in to comment.