From fad41aa1cc31a32c81723c8108ecd42cdf572a89 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Feb 2026 01:06:28 +0000 Subject: [PATCH] Fix JsonValue.Float serialization: append '.0' for whole-number floats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JsonValue.Float(100.0).ToString() previously returned "100" instead of "100.0", discarding the fact that the value is a float. This causes issues when downstream consumers expect a JSON float (not an integer). Root cause: the write path used w.Write(number) for the Float DU case which calls number.ToString() — and (100.0).ToString() returns "100" in .NET. Fix: format the float using the "R" round-trip format and, if the result contains no decimal point or exponent character, append ".0". This matches the behaviour of JsonValue.Parse("100.0") which correctly serialises as "100.0" (because decimal 100M with Scale=1 already has the trailing zero). Closes #1356 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/FSharp.Data.Json.Core/JsonValue.fs | 8 +++++++- tests/FSharp.Data.Core.Tests/JsonValue.fs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Data.Json.Core/JsonValue.fs b/src/FSharp.Data.Json.Core/JsonValue.fs index bc8b7406a..e028641c5 100644 --- a/src/FSharp.Data.Json.Core/JsonValue.fs +++ b/src/FSharp.Data.Json.Core/JsonValue.fs @@ -86,7 +86,13 @@ type JsonValue = | Boolean b -> w.Write(if b then "true" else "false") | Number number -> w.Write number | Float v when Double.IsInfinity v || Double.IsNaN v -> w.Write "null" - | Float number -> w.Write number + | Float number -> + let s = number.ToString("R", CultureInfo.InvariantCulture) + w.Write s + // Ensure the output looks like a float (has a decimal point or exponent), + // so that round-tripping through JSON preserves the Float type. + if s.IndexOfAny([| '.'; 'E'; 'e' |]) = -1 then + w.Write ".0" | String s -> w.Write "\"" JsonValue.JsonStringEncodeTo w s diff --git a/tests/FSharp.Data.Core.Tests/JsonValue.fs b/tests/FSharp.Data.Core.Tests/JsonValue.fs index 6505ad4ae..846061255 100644 --- a/tests/FSharp.Data.Core.Tests/JsonValue.fs +++ b/tests/FSharp.Data.Core.Tests/JsonValue.fs @@ -293,6 +293,20 @@ let ``Serializes special float value as null`` v = let json = JsonValue.Float v json.ToString(JsonSaveOptions.DisableFormatting) |> should equal "null" +[] +let ``Float value 100.0 serializes with decimal point`` () = + // Regression test for https://github.com/fsprojects/FSharp.Data/issues/1356 + // JsonValue.Float(100.0) should serialize as "100.0", not "100" + JsonValue.Float(100.0).ToString(JsonSaveOptions.DisableFormatting) |> should equal "100.0" + +[] +let ``Float value with fractional part serializes correctly`` () = + JsonValue.Float(100.5).ToString(JsonSaveOptions.DisableFormatting) |> should equal "100.5" + +[] +let ``Float value in scientific notation serializes correctly`` () = + JsonValue.Float(1e20).ToString(JsonSaveOptions.DisableFormatting) |> should equal "1E+20" + let normalize (str:string) = str.Replace("\r\n", "\n") .Replace("\r", "\n")