From 50537e8f48ea9499f72ebb77cc19d59bb562d572 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Fri, 18 Sep 2026 15:20:44 +0200 Subject: [PATCH 1/2] Use Span APIs for string case Added System.Memory refs to enable Span usage in string extension methods for first char case changes, increasing efficiency and safety. --- Packages.props | 1 + src/FSharp.Data.GraphQL.Client/Extensions.fs | 12 ++++++++---- .../FSharp.Data.GraphQL.Client.fsproj | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Packages.props b/Packages.props index 6ab045fcc..ba48ae2ca 100644 --- a/Packages.props +++ b/Packages.props @@ -31,6 +31,7 @@ + diff --git a/src/FSharp.Data.GraphQL.Client/Extensions.fs b/src/FSharp.Data.GraphQL.Client/Extensions.fs index 794118f6b..8a97998b1 100644 --- a/src/FSharp.Data.GraphQL.Client/Extensions.fs +++ b/src/FSharp.Data.GraphQL.Client/Extensions.fs @@ -11,14 +11,18 @@ open System.Security.Cryptography /// Extensions for types used by the GraphQL client library. [] module internal Extensions = + + let private changeFirstChar mapping (s : string) = + let span = s.AsSpan() + let c = mapping span[0] + if c = span[0] then s else string c + span.Slice(1).ToString() + type String with /// Returns the input string with the first character in upper case. - member this.FirstCharUpper() = - this.Substring(0, 1).ToUpperInvariant() + this.Substring(1) + member this.FirstCharUpper () = changeFirstChar Char.ToUpperInvariant this /// Returns the input string with the first character in lower case. - member this.FirstCharLower() = - this.Substring(0, 1).ToLowerInvariant() + this.Substring(1) + member this.FirstCharLower () = changeFirstChar Char.ToLowerInvariant this member this.MD5Hash() = Encoding.UTF8.GetBytes(this) diff --git a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj index 46901b937..a67f57de8 100644 --- a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj +++ b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj @@ -69,6 +69,7 @@ runtime + From 06a7fd4ab1c59a18e4c3680a51b249ae125db191 Mon Sep 17 00:00:00 2001 From: Andrii Chebukin Date: Fri, 18 Sep 2026 15:34:41 +0200 Subject: [PATCH 2/2] Switched to `JsonNamingPolicy.CamelCase` for key naming * Replaced custom `FirstCharLower` with `System.Text.Json`'s `JsonNamingPolicy.CamelCase` for property and dictionary key conversion. * Updated serialization and file variable mapping for consistent .NET camelCase conventions. * Added `System.Text.Json` dependency and removed the obsolete extension method. --- src/FSharp.Data.GraphQL.Client/Extensions.fs | 3 --- .../FSharp.Data.GraphQL.Client.fsproj | 1 + src/FSharp.Data.GraphQL.Client/GraphQLClient.fs | 3 ++- src/FSharp.Data.GraphQL.Client/Serialization.fs | 5 +++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Data.GraphQL.Client/Extensions.fs b/src/FSharp.Data.GraphQL.Client/Extensions.fs index 8a97998b1..7b5178c63 100644 --- a/src/FSharp.Data.GraphQL.Client/Extensions.fs +++ b/src/FSharp.Data.GraphQL.Client/Extensions.fs @@ -21,9 +21,6 @@ module internal Extensions = /// Returns the input string with the first character in upper case. member this.FirstCharUpper () = changeFirstChar Char.ToUpperInvariant this - /// Returns the input string with the first character in lower case. - member this.FirstCharLower () = changeFirstChar Char.ToLowerInvariant this - member this.MD5Hash() = Encoding.UTF8.GetBytes(this) |> MD5.Create().ComputeHash diff --git a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj index a67f57de8..6a8bae933 100644 --- a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj +++ b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj @@ -70,6 +70,7 @@ + diff --git a/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs b/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs index 6a757fc2d..7888edaba 100644 --- a/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs +++ b/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs @@ -8,6 +8,7 @@ open System.Collections.Generic open System.Collections.Immutable open System.Net.Http open System.Text +open System.Text.Json open System.Threading open System.Threading.Tasks @@ -131,7 +132,7 @@ module GraphQLClient = | :? IDictionary as x -> x |> Seq.collect (fun kvp -> - tryMapFileVariable (name + "." + (kvp.Key.FirstCharLower ()), kvp.Value) + tryMapFileVariable (name + "." + JsonNamingPolicy.CamelCase.ConvertName kvp.Key, kvp.Value) |> Option.defaultValue [||]) |> Array.ofSeq |> Some diff --git a/src/FSharp.Data.GraphQL.Client/Serialization.fs b/src/FSharp.Data.GraphQL.Client/Serialization.fs index 6572c1aae..68e036482 100644 --- a/src/FSharp.Data.GraphQL.Client/Serialization.fs +++ b/src/FSharp.Data.GraphQL.Client/Serialization.fs @@ -8,6 +8,7 @@ open System.Collections.Generic open System.Diagnostics open System.Globalization open System.Reflection +open System.Text.Json open Microsoft.FSharp.Reflection open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Client.ReflectionPatterns @@ -176,7 +177,7 @@ module Serialization = | :? Upload as u -> JsonValue.String u.Name | :? IDictionary as items -> items - |> Seq.map (fun (KeyValue (k, v)) -> k.FirstCharLower(), toJsonValue v) + |> Seq.map (fun (KeyValue (k, v)) -> JsonNamingPolicy.CamelCase.ConvertName k, toJsonValue v) |> Seq.toArray |> JsonValue.Record | EnumerableValue items -> @@ -187,7 +188,7 @@ module Serialization = | EnumValue x -> JsonValue.String x | _ -> let props = t.GetProperties(BindingFlags.Public ||| BindingFlags.Instance) - let items = props |> Array.map (fun p -> (p.Name.FirstCharLower(), p.GetValue(x) |> toJsonValue)) + let items = props |> Array.map (fun p -> (JsonNamingPolicy.CamelCase.ConvertName p.Name, p.GetValue(x) |> toJsonValue)) JsonValue.Record items) let serializeRecord (x : obj) =