Skip to content

Commit 6e8a0e3

Browse files
authored
Merge pull request #1766 from fsprojects/repo-assist/fix-json-writeto-culture-2026-04-28-09f2ea7bfb7db127
[Repo Assist] fix: JsonValue.WriteTo uses InvariantCulture for decimal numbers
2 parents a081ef9 + 42e1e57 commit 6e8a0e3

3 files changed

Lines changed: 40 additions & 2 deletions

File tree

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Release Notes
22

3+
## 8.1.12 - Apr 28 2026
4+
5+
- Fix: `JsonValue.WriteTo` now always uses `CultureInfo.InvariantCulture` when serializing `Number` (decimal) values, preventing invalid JSON output (e.g. `1,5` instead of `1.5`) when called with a `TextWriter` configured with a non-English culture.
6+
- Perf: `JsonValue.WriteTo` no longer allocates an intermediate `System.String(' ', n)` per indentation level; spaces are written directly to the writer.
7+
38
## 8.1.11 - Apr 22 2026
49

510
- Code: `HtmlParser` `EmitTag` removes dead code in the `else` branch (the expression `x.HasFormattedParent || x.IsFormattedTag` was always equivalent to `x.HasFormattedParent` since `x.IsFormattedTag` is always `false` in that branch). Uses `name` directly to avoid re-computing `CurrentTagName()` for formatted/script tag checks. Also removes redundant `.ToLowerInvariant()` calls in `IsFormattedTag` and `IsScriptTag` since tag names are already lowercased at read time.

src/FSharp.Data.Json.Core/JsonValue.fs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,16 @@ type JsonValue =
7373
member x.WriteTo(w: TextWriter, saveOptions, ?indentationSpaces: int) =
7474
let indentationSpaces = defaultArg indentationSpaces 2
7575

76+
// Write `count` space characters without allocating an intermediate string.
77+
let inline writeSpaces count =
78+
for _ = 1 to count do
79+
w.Write(' ')
80+
7681
let newLine =
7782
if saveOptions = JsonSaveOptions.None then
7883
fun indentation plus ->
7984
w.WriteLine()
80-
System.String(' ', indentation + plus) |> w.Write
85+
writeSpaces (indentation + plus)
8186
else
8287
fun _ _ -> ()
8388

@@ -94,7 +99,7 @@ type JsonValue =
9499
function
95100
| Null -> w.Write "null"
96101
| Boolean b -> w.Write(if b then "true" else "false")
97-
| Number number -> w.Write number
102+
| Number number -> w.Write(number.ToString(CultureInfo.InvariantCulture))
98103
| Float v when Double.IsInfinity v || Double.IsNaN v -> w.Write "null"
99104
| Float number ->
100105
let s = number.ToString("R", CultureInfo.InvariantCulture)

tests/FSharp.Data.Core.Tests/JsonValue.fs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,3 +859,31 @@ let ``JsonValue WriteTo with None (default) produces indented output`` () =
859859
let result = writer.ToString()
860860
result.Contains("\n") |> should equal true
861861
result.Contains(" ") |> should equal true
862+
863+
[<Test>]
864+
let ``JsonValue WriteTo serializes decimals using InvariantCulture regardless of thread culture`` () =
865+
// In cultures that use ',' as decimal separator (e.g. de-DE), TextWriter.Write(decimal)
866+
// could produce invalid JSON like {"price":1,5} instead of {"price":1.5}.
867+
// WriteTo must always use InvariantCulture for decimal numbers.
868+
use _holder = withCulture "de-DE"
869+
let json = JsonValue.Record [| "price", JsonValue.Number 1.5M |]
870+
use writer = new System.IO.StringWriter()
871+
json.WriteTo(writer, JsonSaveOptions.DisableFormatting)
872+
let result = writer.ToString()
873+
result |> should equal """{"price":1.5}"""
874+
875+
[<Test>]
876+
let ``JsonValue ToString serializes decimal array using InvariantCulture`` () =
877+
use _holder = withCulture "fr-FR"
878+
let json = JsonValue.Array [| JsonValue.Number 1.5M; JsonValue.Number 99.99M |]
879+
json.ToString(JsonSaveOptions.DisableFormatting)
880+
|> should equal "[1.5,99.99]"
881+
882+
[<Test>]
883+
let ``JsonValue WriteTo indentation uses correct number of spaces`` () =
884+
let json = JsonValue.Record [| "x", JsonValue.Number 1M |]
885+
use writer = new System.IO.StringWriter()
886+
json.WriteTo(writer, JsonSaveOptions.None, 4)
887+
let result = writer.ToString()
888+
// With 4-space indent, the property line should start with 4 spaces
889+
result |> should contain " \"x\""

0 commit comments

Comments
 (0)