diff --git a/GraphQLSharp.Tests/GraphQLClientTests.cs b/GraphQLSharp.Tests/GraphQLClientTests.cs index 837c4ee..76b6988 100644 --- a/GraphQLSharp.Tests/GraphQLClientTests.cs +++ b/GraphQLSharp.Tests/GraphQLClientTests.cs @@ -228,9 +228,9 @@ public async Task QueryWithAliases() var response = await _client.ExecuteAsync(request); //response.data is JsonElement var myProducts = response.data.Value.GetProperty("myProducts") - .Deserialize(Serializer.Options); + .Deserialize(Serializer.GetOptions()); var myOrders = response.data.Value.GetProperty("myOrders") - .Deserialize(Serializer.Options); + .Deserialize(Serializer.GetOptions()); Assert.IsNotNull(myProducts.nodes.FirstOrDefault()?.title); Assert.IsNotNull(myOrders.nodes.FirstOrDefault()?.name); } diff --git a/GraphQLSharp.Tests/SerializationTests.cs b/GraphQLSharp.Tests/SerializationTests.cs index b8f0b67..0e8714f 100644 --- a/GraphQLSharp.Tests/SerializationTests.cs +++ b/GraphQLSharp.Tests/SerializationTests.cs @@ -6,7 +6,7 @@ namespace GraphQLSharp.Tests; [TestClass] public class SerializationTests { - private class MyObject + private class MyDateTimeObject { public DateTime at { get; set; } public DateTime? atNullable { get; set; } @@ -16,6 +16,13 @@ private class MyObject public DateTimeOffset? atOffsetNullable2 { get; set; } } + private class MyBigIntObject + { + public long longValue { get; set; } + public ulong ulongValue { get; set; } + public int intValue { get; set; } + public uint uintValue { get; set; } + } [TestMethod] public void DeserializeDateTimePropertyValid() @@ -32,7 +39,7 @@ public void DeserializeDateTimePropertyValid() } """; - MyObject result = JsonSerializer.Deserialize(json, Serializer.Options); + MyDateTimeObject result = JsonSerializer.Deserialize(json, Serializer.GetOptions()); Assert.AreEqual(now, result.at); Assert.AreEqual(now, result.atNullable); Assert.AreEqual(nowOffset, result.atOffset); @@ -52,7 +59,7 @@ public void DeserializeDateTimePropertyMinValue() "atOffsetNullable2": null } """; - MyObject result = JsonSerializer.Deserialize(json, Serializer.Options); + MyDateTimeObject result = JsonSerializer.Deserialize(json, Serializer.GetOptions()); Assert.AreEqual(DateTime.MinValue, result.at); Assert.AreEqual(DateTime.MinValue, result.atNullable); Assert.IsNull(result.atNullable2); @@ -71,6 +78,43 @@ public void DeserializeDateTimePropertyInvalid() "atNullable": "invalid-date", } """; - JsonSerializer.Deserialize(json, Serializer.Options); + JsonSerializer.Deserialize(json, Serializer.GetOptions()); + } + + [TestMethod] + public void DeserializeBigInt() + { + string json = $$""" + { + "longValue": "9223372036854775807", + "ulongValue": "18446744073709551615", + "intValue": 2147483647, + "uintValue": 4294967295 + } + """; + + MyBigIntObject result = JsonSerializer.Deserialize(json, Serializer.GetOptions()); + Assert.AreEqual(9223372036854775807, result.longValue); + Assert.AreEqual(18446744073709551615, result.ulongValue); + Assert.AreEqual(2147483647, result.intValue); + Assert.AreEqual(4294967295, result.uintValue); + } + + [TestMethod] + public void SerializeBigInt() + { + var obj = new MyBigIntObject + { + longValue = 9223372036854775807, + ulongValue = 18446744073709551615, + intValue = 2147483647, + uintValue = 4294967295 + }; + + string json = JsonSerializer.Serialize(obj, Serializer.GetOptions()); + Assert.IsTrue(json.Contains(@"""longValue"":""9223372036854775807""")); + Assert.IsTrue(json.Contains(@"""ulongValue"":""18446744073709551615""")); + Assert.IsTrue(json.Contains(@"""intValue"":2147483647")); + Assert.IsTrue(json.Contains(@"""uintValue"":4294967295")); } } \ No newline at end of file diff --git a/GraphQLSharp/Client/GraphQLClient.cs b/GraphQLSharp/Client/GraphQLClient.cs index affd1ed..1d5a9f3 100644 --- a/GraphQLSharp/Client/GraphQLClient.cs +++ b/GraphQLSharp/Client/GraphQLClient.cs @@ -108,7 +108,7 @@ private async Task> ExecuteCoreAsync(TRequest request, Can GraphQLResponse res; try { - res = await httpResponseMsg.Content.ReadFromJsonAsync>(_options.JsonSerializerOptions ?? Serializer.Options, cancellationToken); + res = await httpResponseMsg.Content.ReadFromJsonAsync>(_options.JsonSerializerOptions ?? Serializer.GetOptions(), cancellationToken); if (res == null) throw new GraphQLException(request, httpResponse, $"Failed to deserialize null GraphQL response. Request: {request}"); } @@ -139,7 +139,7 @@ private HttpRequestMessage CreateHttpRequest(TRequest request) { Method = HttpMethod.Post, RequestUri = uri, - Content = JsonContent.Create(request, options: _options.JsonSerializerOptions ?? Serializer.Options), + Content = JsonContent.Create(request, options: _options.JsonSerializerOptions ?? Serializer.GetOptions()), }; requestMessage.Headers.UserAgent.Add(_defaultUserAgent); diff --git a/GraphQLSharp/GraphQLSharp.csproj b/GraphQLSharp/GraphQLSharp.csproj index 46872f8..a1d4178 100644 --- a/GraphQLSharp/GraphQLSharp.csproj +++ b/GraphQLSharp/GraphQLSharp.csproj @@ -4,7 +4,7 @@ https://github.com/Wish-Org/GraphQLSharp https://github.com/Wish-Org/GraphQLSharp .NET Client for GraphQL - Modern and fast - 2.20.0 + 2.21.0 graphql;client;graphql-client;graphql-generator Wish-Org README.md diff --git a/GraphQLSharp/Serialization/BigIntConverter.cs b/GraphQLSharp/Serialization/BigIntConverter.cs new file mode 100644 index 0000000..fcc713f --- /dev/null +++ b/GraphQLSharp/Serialization/BigIntConverter.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +public class Int64ToStringConverter : BigIntToStringConverter +{ +} + +public class UInt64ToStringConverter : BigIntToStringConverter +{ +} + +public abstract class BigIntToStringConverter : JsonConverter +{ + private readonly static JsonConverter _defaultConverter = (JsonConverter)JsonSerializerOptions.Default.GetConverter(typeof(T)); + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + return (T)Convert.ChangeType(reader.GetString(), typeof(T)); + + return JsonSerializer.Deserialize(ref reader); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} \ No newline at end of file diff --git a/GraphQLSharp/Serialization/Serializer.cs b/GraphQLSharp/Serialization/Serializer.cs index a0f6b71..bc2774f 100644 --- a/GraphQLSharp/Serialization/Serializer.cs +++ b/GraphQLSharp/Serialization/Serializer.cs @@ -1,5 +1,6 @@ using System.Text.Json.Serialization; using System.Text.Json; +using System.Collections.Concurrent; namespace GraphQLSharp; @@ -7,44 +8,52 @@ namespace GraphQLSharp; public static class Serializer { - public static readonly JsonSerializerOptions Options; - - public static readonly JsonSerializerOptions OptionsIndented; + private static readonly ConcurrentDictionary<(bool indent, bool serializeInt64ToString), JsonSerializerOptions> optionstoJsonOptions = new(); static Serializer() { - Options = new JsonSerializerOptions + } + + public static JsonSerializerOptions CreateOptions(bool indent, bool serializeInt64ToString) + { + var options = new JsonSerializerOptions { NumberHandling = JsonNumberHandling.AllowReadingFromString, Converters = { new JsonStringEnumConverter() }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; - Options.Converters.Add(new SafeDateTimeConverter()); - Options.Converters.Add(new SafeDateTimeOffsetConverter()); + options.Converters.Add(new SafeDateTimeConverter()); + options.Converters.Add(new SafeDateTimeOffsetConverter()); - OptionsIndented = new JsonSerializerOptions(Options) + if (serializeInt64ToString) { - WriteIndented = true - }; + options.Converters.Add(new Int64ToStringConverter()); + options.Converters.Add(new UInt64ToStringConverter()); + } + + if (indent) + options.WriteIndented = true; + + return options; } - public static JsonSerializerOptions GetOptions(bool indent) + public static JsonSerializerOptions GetOptions(bool indent = false, bool serializeInt64ToString = true) { - return indent ? OptionsIndented : Options; + return optionstoJsonOptions.GetOrAdd((indent, serializeInt64ToString), _ => CreateOptions(indent, serializeInt64ToString)); } - public static string Serialize(object obj, bool indent = false) + public static string Serialize(object obj, bool indent = false, bool serializeInt64ToString = true) { - return JsonSerializer.Serialize(obj, obj.GetType(), GetOptions(indent)); + return JsonSerializer.Serialize(obj, obj.GetType(), GetOptions(indent, serializeInt64ToString)); } - public static object? Deserialize(string json, Type type, bool indent = false) + public static object? Deserialize(string json, Type type, bool indent = false, bool serializeInt64ToString = true) { - return JsonSerializer.Deserialize(json, type, GetOptions(indent)); + return JsonSerializer.Deserialize(json, type, GetOptions(indent, serializeInt64ToString)); } - public static T? Deserialize(string json, bool indent = false) + public static T? Deserialize(string json, bool indent = false, bool serializeInt64ToString = true) { - return JsonSerializer.Deserialize(json, GetOptions(indent)); + return JsonSerializer.Deserialize(json, GetOptions(indent, serializeInt64ToString)); } } \ No newline at end of file