diff --git a/samples/JsonTranscoding/JsonTranscodingSample.Shared/IMyFirstService.cs b/samples/JsonTranscoding/JsonTranscodingSample.Shared/IMyFirstService.cs index 5e69c25a..91424e13 100644 --- a/samples/JsonTranscoding/JsonTranscodingSample.Shared/IMyFirstService.cs +++ b/samples/JsonTranscoding/JsonTranscodingSample.Shared/IMyFirstService.cs @@ -3,7 +3,7 @@ namespace JsonTranscodingSample.Shared; -public interface IMyFirstService +public interface IMyFirstService : IService { UnaryResult SayHelloAsync(string name, int age); UnaryResult RegisterUserAsync(RegisterUserRequest request); diff --git a/src/MagicOnion.Server.JsonTranscoding/MagicOnion.Server.JsonTranscoding.csproj b/src/MagicOnion.Server.JsonTranscoding/MagicOnion.Server.JsonTranscoding.csproj index 85b8030f..6991eb22 100644 --- a/src/MagicOnion.Server.JsonTranscoding/MagicOnion.Server.JsonTranscoding.csproj +++ b/src/MagicOnion.Server.JsonTranscoding/MagicOnion.Server.JsonTranscoding.csproj @@ -18,4 +18,8 @@ + + + + diff --git a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs index fbbf349d..90cb6961 100644 --- a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs +++ b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingGrpcMethodBinder.cs @@ -23,7 +23,7 @@ ILoggerFactory loggerFactory { public void BindUnary(IMagicOnionUnaryMethod method) where TRawRequest : class where TRawResponse : class { - var messageSerializer = new MessagePackJsonMessageSerializer(options.MessagePackSerializerOptions ?? MessagePackSerializer.DefaultOptions); + var messageSerializer = new SystemTextJsonMessageSerializer(options.JsonSerializerOptions ?? JsonSerializerOptions.Default); var grpcMethod = GrpcMethodHelper.CreateMethod(MethodType.Unary, method.ServiceName, method.MethodName, messageSerializer); @@ -36,6 +36,9 @@ public void BindUnary(IMagicOnio context.AddMethod(grpcMethod, RoutePatternFactory.Parse(routePath), metadata, async (context) => { var serverCallContext = new MagicOnionJsonTranscodingServerCallContext(method); + + // Grpc.AspNetCore.Server expects that UserState has the key "__HttpContext" and that HttpContext is set to it. + // https://github.com/grpc/grpc-dotnet/blob/5a58c24efc1d0b7c5ff88e7b0582ea891b90b17f/src/Grpc.AspNetCore.Server/ServerCallContextExtensions.cs#L30 serverCallContext.UserState["__HttpContext"] = context; context.Features.Set(serverCallContext); diff --git a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingOptions.cs b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingOptions.cs index c86fb41e..bbe8f6fc 100644 --- a/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingOptions.cs +++ b/src/MagicOnion.Server.JsonTranscoding/MagicOnionJsonTranscodingOptions.cs @@ -1,8 +1,8 @@ -using MessagePack; +using System.Text.Json; namespace MagicOnion.Server.JsonTranscoding; public class MagicOnionJsonTranscodingOptions { - public MessagePackSerializerOptions? MessagePackSerializerOptions { get; set; } + public JsonSerializerOptions? JsonSerializerOptions { get; set; } } diff --git a/src/MagicOnion.Server.JsonTranscoding/MessagePackJsonMessageSerializer.cs b/src/MagicOnion.Server.JsonTranscoding/MessagePackJsonMessageSerializer.cs deleted file mode 100644 index 81d175a0..00000000 --- a/src/MagicOnion.Server.JsonTranscoding/MessagePackJsonMessageSerializer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using MagicOnion.Serialization; -using System.Buffers; -using System.Text; -using MagicOnion.Internal.Buffers; -using MessagePack; - -namespace MagicOnion.Server.JsonTranscoding; - -public class MessagePackJsonMessageSerializer : IMagicOnionSerializer -{ - readonly MessagePackSerializerOptions options; - - public MessagePackJsonMessageSerializer(MessagePackSerializerOptions options) - { - this.options = options; - } - - public void Serialize(IBufferWriter writer, in T value) - { - using var bufferWriter = ArrayPoolBufferWriter.RentThreadStaticWriter(); - MessagePackSerializer.Serialize(bufferWriter, value, options); - - var json = MessagePackSerializer.ConvertToJson(bufferWriter.WrittenMemory, options); - writer.Write(Encoding.UTF8.GetBytes(json)); - } - - public T Deserialize(in ReadOnlySequence bytes) - { - var messagePackBytes = MessagePackSerializer.ConvertFromJson(Encoding.UTF8.GetString(bytes.ToArray())); - return MessagePackSerializer.Deserialize(messagePackBytes, options); - } -} diff --git a/src/MagicOnion.Server.JsonTranscoding/SystemTextJsonMessageSerializer.cs b/src/MagicOnion.Server.JsonTranscoding/SystemTextJsonMessageSerializer.cs new file mode 100644 index 00000000..130e3563 --- /dev/null +++ b/src/MagicOnion.Server.JsonTranscoding/SystemTextJsonMessageSerializer.cs @@ -0,0 +1,652 @@ +using MagicOnion.Serialization; +using System.Buffers; +using System.Text.Json; +using System.Text.Json.Serialization; +using MessagePack; + +namespace MagicOnion.Server.JsonTranscoding; + +internal class SystemTextJsonMessageSerializer : IMagicOnionSerializer +{ + readonly JsonSerializerOptions options; + + public SystemTextJsonMessageSerializer(JsonSerializerOptions options) + { + this.options = new JsonSerializerOptions(options); + this.options.Converters.Add(NilConverter.Instance); + this.options.Converters.Add(DynamicArgumentTupleConverterFactory.Instance); + } + + public void Serialize(IBufferWriter writer, in T value) + { + JsonSerializer.Serialize(new Utf8JsonWriter(writer), value, options); + } + + public T Deserialize(in ReadOnlySequence bytes) + { + var reader = new Utf8JsonReader(bytes); + return JsonSerializer.Deserialize(ref reader, options)!; + } + + class NilConverter : JsonConverter + { + public static NilConverter Instance { get; } = new(); + + public override Nil Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Skip(); + return Nil.Default; + } + + public override void Write(Utf8JsonWriter writer, Nil value, JsonSerializerOptions options) + { + writer.WriteNullValue(); + } + } + + class DynamicArgumentTupleConverterFactory : JsonConverterFactory + { + public static DynamicArgumentTupleConverterFactory Instance { get; } = new(); + + public override bool CanConvert(Type typeToConvert) + { + if (!typeToConvert.IsGenericType) return false; + var openType = typeToConvert.GetGenericTypeDefinition(); + + return openType == typeof(DynamicArgumentTuple<,>) || + openType == typeof(DynamicArgumentTuple<,,>) || + openType == typeof(DynamicArgumentTuple<,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,,,,,>) || + openType == typeof(DynamicArgumentTuple<,,,,,,,,,,,,,,>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var openType = typeToConvert.GetGenericTypeDefinition()!; + var genericArguments = typeToConvert.GetGenericArguments(); + + var converterType = genericArguments.Length switch + { + 2 => typeof(DynamicArgumentTupleConverter<,>).MakeGenericType(genericArguments), + 3 => typeof(DynamicArgumentTupleConverter<,,>).MakeGenericType(genericArguments), + 4 => typeof(DynamicArgumentTupleConverter<,,,>).MakeGenericType(genericArguments), + 5 => typeof(DynamicArgumentTupleConverter<,,,,>).MakeGenericType(genericArguments), + 6 => typeof(DynamicArgumentTupleConverter<,,,,,>).MakeGenericType(genericArguments), + 7 => typeof(DynamicArgumentTupleConverter<,,,,,,>).MakeGenericType(genericArguments), + 8 => typeof(DynamicArgumentTupleConverter<,,,,,,,>).MakeGenericType(genericArguments), + 9 => typeof(DynamicArgumentTupleConverter<,,,,,,,,>).MakeGenericType(genericArguments), + 10 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,>).MakeGenericType(genericArguments), + 11 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,,>).MakeGenericType(genericArguments), + 12 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,,,>).MakeGenericType(genericArguments), + 13 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,,,,>).MakeGenericType(genericArguments), + 14 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,,,,,>).MakeGenericType(genericArguments), + 15 => typeof(DynamicArgumentTupleConverter<,,,,,,,,,,,,,,>).MakeGenericType(genericArguments), + _ => throw new NotSupportedException() + }; + + return (JsonConverter)Activator.CreateInstance(converterType)!; + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item11 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!, item11!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + JsonSerializer.Serialize(writer, value.Item11, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item11 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item12 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!, item11!, item12!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + JsonSerializer.Serialize(writer, value.Item11, options); + JsonSerializer.Serialize(writer, value.Item12, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item11 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item12 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item13 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!, item11!, item12!, item13!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + JsonSerializer.Serialize(writer, value.Item11, options); + JsonSerializer.Serialize(writer, value.Item12, options); + JsonSerializer.Serialize(writer, value.Item13, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item11 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item12 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item13 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item14 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!, item11!, item12!, item13!, item14!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + JsonSerializer.Serialize(writer, value.Item11, options); + JsonSerializer.Serialize(writer, value.Item12, options); + JsonSerializer.Serialize(writer, value.Item13, options); + JsonSerializer.Serialize(writer, value.Item14, options); + writer.WriteEndArray(); + } + } + class DynamicArgumentTupleConverter : JsonConverter> + { + public override DynamicArgumentTuple Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + reader.Read(); + var item1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item3 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item4 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item5 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item6 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item7 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item8 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item9 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item10 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item11 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item12 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item13 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item14 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + var item15 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new DynamicArgumentTuple(item1!, item2!, item3!, item4!, item5!, item6!, item7!, item8!, item9!, item10!, item11!, item12!, item13!, item14!, item15!); + } + + public override void Write(Utf8JsonWriter writer, DynamicArgumentTuple value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + JsonSerializer.Serialize(writer, value.Item1, options); + JsonSerializer.Serialize(writer, value.Item2, options); + JsonSerializer.Serialize(writer, value.Item3, options); + JsonSerializer.Serialize(writer, value.Item4, options); + JsonSerializer.Serialize(writer, value.Item5, options); + JsonSerializer.Serialize(writer, value.Item6, options); + JsonSerializer.Serialize(writer, value.Item7, options); + JsonSerializer.Serialize(writer, value.Item8, options); + JsonSerializer.Serialize(writer, value.Item9, options); + JsonSerializer.Serialize(writer, value.Item10, options); + JsonSerializer.Serialize(writer, value.Item11, options); + JsonSerializer.Serialize(writer, value.Item12, options); + JsonSerializer.Serialize(writer, value.Item13, options); + JsonSerializer.Serialize(writer, value.Item14, options); + JsonSerializer.Serialize(writer, value.Item15, options); + writer.WriteEndArray(); + } + } + } +} diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj b/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj index d0d2b65e..cd75ea3a 100644 --- a/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj +++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/MagicOnion.Server.JsonTranscoding.Tests.csproj @@ -7,6 +7,9 @@ false true + + true + ..\..\src\MagicOnion\opensource.snk diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/SystemTextJsonMessageSerializerTest.cs b/tests/MagicOnion.Server.JsonTranscoding.Tests/SystemTextJsonMessageSerializerTest.cs new file mode 100644 index 00000000..ba33365b --- /dev/null +++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/SystemTextJsonMessageSerializerTest.cs @@ -0,0 +1,272 @@ +using System.Buffers; +using System.Text; +using System.Text.Json; +using MessagePack; + +namespace MagicOnion.Server.JsonTranscoding.Tests; + +public class SystemTextJsonMessageSerializerTest +{ + [Fact] + public void Serialize_Primitive_BuiltIn_String() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, "FooBar"); + + // Assert + Assert.Equal("\"FooBar\""u8.ToArray(), writer.WrittenMemory.ToArray()); + } + + [Fact] + public void Serialize_Primitive_BuiltIn_Number() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, 123456789); + + // Assert + Assert.Equal("123456789"u8.ToArray(), writer.WrittenMemory.ToArray()); + } + + [Fact] + public void Serialize_Primitive_BuiltIn_Null() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, default(string)); + + // Assert + Assert.Equal("null"u8.ToArray(), writer.WrittenMemory.ToArray()); + } + + [Fact] + public void Serialize_Primitive_Nil() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, Nil.Default); + + // Assert + Assert.Equal("null"u8.ToArray(), writer.WrittenMemory.ToArray()); + } + + [Fact] + public void Serialize_DynamicArgumentTuple_2() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, new DynamicArgumentTuple("Alice", 18)); + + // Assert + Assert.Equal("""["Alice",18]""", Encoding.UTF8.GetString(writer.WrittenMemory.Span)); + } + + [Fact] + public void Serialize_DynamicArgumentTuple_15() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, new DynamicArgumentTuple( + "Alice", 18, true, long.MaxValue, 'X', int.MaxValue, "Foo", false, Guid.Parse("4dfa3247-0686-4b07-9772-cfb7ef30c22c"), 255, "Bar", 12345, -12345, 3.14f)); + + // Assert + Assert.Equal("""["Alice",18,true,9223372036854775807,"X",2147483647,"Foo",false,"4dfa3247-0686-4b07-9772-cfb7ef30c22c",255,"Bar",12345,-12345,3.14]""", Encoding.UTF8.GetString(writer.WrittenMemory.Span)); + } + + [Fact] + public void Serialize_Complex() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, new TestResponse() + { + A = 1234, + B = "Alice", + C = true, + Inner = new TestResponse.InnerResponse() + { + D = 98765432100, + E = "Hello!", + }, + }); + + // Assert + Assert.Equal("""{"A":1234,"B":"Alice","C":true,"Inner":{"D":98765432100,"E":"Hello!"}}""", Encoding.UTF8.GetString(writer.WrittenMemory.Span)); + } + + [Fact] + public void Deserialize_Primitive_BuiltIn_String() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize(new ReadOnlySequence("\"FooBar\""u8.ToArray())); + + // Assert + Assert.Equal("FooBar", value); + } + + [Fact] + public void Deserialize_Primitive_BuiltIn_Number() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize(new ReadOnlySequence("""123456789"""u8.ToArray())); + + // Assert + Assert.Equal(123456789, value); + } + + [Fact] + public void Deserialize_Primitive_BuiltIn_Null() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize(new ReadOnlySequence("""null"""u8.ToArray())); + + // Assert + Assert.Null(value); + } + + [Fact] + public void Deserialize_Primitive_Nil() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize(new ReadOnlySequence("""null"""u8.ToArray())); + + // Assert + Assert.Equal(Nil.Default, value); + } + + [Fact] + public void Deserialize_DynamicArgumentTuple_2() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize>(new ReadOnlySequence("""["Alice",18]"""u8.ToArray())); + + // Assert + Assert.Equal("Alice", value.Item1); + Assert.Equal(18, value.Item2); + } + + [Fact] + public void Deserialize_DynamicArgumentTuple_15() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + + // Act + var value = messageSerializer.Deserialize>(new ReadOnlySequence( + """["Alice",18,true,9223372036854775807,"X",2147483647,"Foo",false,"4dfa3247-0686-4b07-9772-cfb7ef30c22c",255,"Bar",12345,-12345,3.14]"""u8.ToArray())); + + // Assert + Assert.Equal("Alice", value.Item1); + Assert.Equal(18, value.Item2); + Assert.True(value.Item3); + Assert.Equal(long.MaxValue, value.Item4); + Assert.Equal('X', value.Item5); + Assert.Equal(int.MaxValue, value.Item6); + Assert.Equal("Foo", value.Item7); + Assert.False(value.Item8); + Assert.Equal(Guid.Parse("{4dfa3247-0686-4b07-9772-cfb7ef30c22c}"), value.Item9); + Assert.Equal(255, value.Item10); + Assert.Equal("Bar", value.Item11); + Assert.Equal(12345, value.Item12); + Assert.Equal(-12345, value.Item13); + Assert.Equal(3.14, value.Item14, precision: 5); + } + + [Fact] + public void Deserialize_Complex() + { + // Arrange + var options = JsonSerializerOptions.Default; + var messageSerializer = new SystemTextJsonMessageSerializer(options); + var writer = new ArrayBufferWriter(); + + // Act + messageSerializer.Serialize(writer, new TestResponse() + { + A = 1234, + B = "Alice", + C = true, + Inner = new TestResponse.InnerResponse() + { + D = 98765432100, + E = "Hello!", + }, + }); + + // Assert + Assert.Equal("""{"A":1234,"B":"Alice","C":true,"Inner":{"D":98765432100,"E":"Hello!"}}""", Encoding.UTF8.GetString(writer.WrittenMemory.Span)); + } + + + [MessagePackObject] + public class TestResponse + { + [Key(0)] + public int A { get; set; } + [Key(1)] + public required string B { get; init; } + [Key(2)] + public bool C { get; set; } + [Key(3)] + public required InnerResponse Inner { get; init; } + + [MessagePackObject] + public class InnerResponse + { + [Key(0)] + public long D { get; set; } + [Key(1)] + public required string E { get; init; } + } + } +} diff --git a/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs b/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs index c60214a9..79d49178 100644 --- a/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs +++ b/tests/MagicOnion.Server.JsonTranscoding.Tests/UnaryFunctionalTests.cs @@ -68,9 +68,75 @@ public async Task Method_NoParameter_ResultComplexType() var content = await response.Content.ReadAsStringAsync(); // Assert - object[] result = [1234, "Alice", true, new object[] { 98765432100, "Hello!" }]; + Assert.Equivalent(new TestResponse() + { + A = 1234, + B = "Alice", + C = true, + Inner = new TestResponse.InnerResponse() + { + D = 98765432100, + E = "Hello!", + }, + }, JsonSerializer.Deserialize(content)); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("application/json", response.Content.Headers.ContentType?.ToString()); + } + + [Fact] + public async Task Method_OneParameter_NoResult() + { + // Arrange + var httpClient = factory.CreateDefaultClient(); + var requestBody = """ + "Alice" + """; + + // Act + var response = await httpClient.PostAsync($"http://localhost/_/ITestService/Method_OneParameter_NoResult", new StringContent(requestBody, new MediaTypeHeaderValue("application/json"))); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("Alice", JsonSerializer.Deserialize(content)); + Assert.Equal("application/json", response.Content.Headers.ContentType?.ToString()); + } + + [Fact] + public async Task Method_TwoParameter_NoResult() + { + // Arrange + var httpClient = factory.CreateDefaultClient(); + var requestBody = """ + ["Alice", 18] + """; + + // Act + var response = await httpClient.PostAsync($"http://localhost/_/ITestService/Method_TwoParameter_NoResult", new StringContent(requestBody, new MediaTypeHeaderValue("application/json"))); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("Alice;18", JsonSerializer.Deserialize(content)); + Assert.Equal("application/json", response.Content.Headers.ContentType?.ToString()); + } + + [Fact] + public async Task Method_ManyParameter_NoResult() + { + // Arrange + var httpClient = factory.CreateDefaultClient(); + var requestBody = """ + ["Alice", 18, true, 128, 3.14, null] + """; + + // Act + var response = await httpClient.PostAsync($"http://localhost/_/ITestService/Method_ManyParameter_NoResult", new StringContent(requestBody, new MediaTypeHeaderValue("application/json"))); + var content = await response.Content.ReadAsStringAsync(); + + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(JsonSerializer.Serialize(result), JsonSerializer.Serialize(JsonSerializer.Deserialize(content))); + Assert.Equal("Alice;18;True;128;3.14;null", JsonSerializer.Deserialize(content)); Assert.Equal("application/json", response.Content.Headers.ContentType?.ToString()); } } @@ -80,6 +146,10 @@ public interface ITestService : IService UnaryResult Method_NoParameter_NoResult(); UnaryResult Method_NoParameter_ResultRefType(); UnaryResult Method_NoParameter_ResultComplexType(); + + UnaryResult Method_OneParameter_NoResult(string name); + UnaryResult Method_TwoParameter_NoResult(string name, int age); + UnaryResult Method_ManyParameter_NoResult(string arg1, int arg2, bool arg3, byte arg4, float arg5, string arg6); } [MessagePackObject] @@ -112,7 +182,7 @@ public UnaryResult Method_NoParameter_NoResult() return default; } -public UnaryResult Method_NoParameter_ResultRefType() + public UnaryResult Method_NoParameter_ResultRefType() => UnaryResult.FromResult(nameof(Method_NoParameter_ResultRefType)); public UnaryResult Method_NoParameter_ResultComplexType() @@ -127,4 +197,13 @@ public UnaryResult Method_NoParameter_ResultComplexType() E = "Hello!", }, }); + + + public UnaryResult Method_OneParameter_NoResult(string name) + => UnaryResult.FromResult($"{name}"); + public UnaryResult Method_TwoParameter_NoResult(string name, int age) + => UnaryResult.FromResult($"{name};{age}"); + public UnaryResult Method_ManyParameter_NoResult(string arg1, int arg2, bool arg3, byte arg4, float arg5, string arg6) + => UnaryResult.FromResult($"{arg1};{arg2};{arg3};{arg4};{arg5};{arg6 ?? "null"}"); + }