diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5ca6d71b7..9c050a235 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,10 @@ # Release Notes -## 8.1.12 - Apr 28 2026 +## 8.1.13 - May 08 2026 + +- Performance: `HtmlNode.serialize` no longer allocates a temporary `string` on each newline/indentation step; uses `StringBuilder.Append(char, int)` overload directly. `isVoidElement` set is now computed once at module initialisation instead of being re-created on every `HtmlNode.ToString()` call. `HtmlDocument.ToString()` uses a single `StringBuilder` for the whole document instead of `List.map … |> String.Concat`. + +## 8.1.12 - Apr 29 2026 - 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. - Perf: `JsonValue.WriteTo` no longer allocates an intermediate `System.String(' ', n)` per indentation level; spaces are written directly to the writer. diff --git a/src/FSharp.Data.Html.Core/HtmlNode.fs b/src/FSharp.Data.Html.Core/HtmlNode.fs index 13485c6bb..5db6e8cef 100644 --- a/src/FSharp.Data.Html.Core/HtmlNode.fs +++ b/src/FSharp.Data.Html.Core/HtmlNode.fs @@ -7,6 +7,33 @@ open System.Text // -------------------------------------------------------------------------------------- +[] +module private HtmlNodeHelpers = + // Void elements per the HTML spec — stored as a module-level constant so the Set is not + // re-created on every HtmlNode.ToString() call. + let private htmlVoidElementSet = + Set.ofArray + [| "area" + "base" + "br" + "col" + "command" + "embed" + "hr" + "img" + "input" + "keygen" + "link" + "meta" + "param" + "source" + "track" + "wbr" |] + + let isVoidElement name = Set.contains name htmlVoidElementSet + +// -------------------------------------------------------------------------------------- + /// Represents an HTML attribute. The name is always normalized to lowercase /// /// Contains the primary types for the FSharp.Data package. @@ -92,28 +119,6 @@ type HtmlNode = static member NewCData content = HtmlCData(content) override x.ToString() = - let isVoidElement = - let set = - [| "area" - "base" - "br" - "col" - "command" - "embed" - "hr" - "img" - "input" - "keygen" - "link" - "meta" - "param" - "source" - "track" - "wbr" |] - |> Set.ofArray - - fun name -> Set.contains name set - let rec serialize (sb: StringBuilder) indentation canAddNewLine insidePre html = let append (str: string) = sb.Append str |> ignore @@ -124,7 +129,7 @@ type HtmlNode = let newLine plus = sb.AppendLine() |> ignore - String(' ', indentation + plus) |> append + sb.Append(' ', indentation + plus) |> ignore match html with | HtmlElement(name, attributes, elements) -> @@ -224,11 +229,17 @@ type HtmlDocument = override x.ToString() = match x with | HtmlDocument(docType, elements) -> - (if String.IsNullOrEmpty docType then - "" - else - "" + Environment.NewLine) - + (elements |> List.map (fun x -> x.ToString()) |> String.Concat) + let sb = StringBuilder() + + if not (String.IsNullOrEmpty docType) then + sb.Append(" ignore + sb.Append(docType) |> ignore + sb.AppendLine(">") |> ignore + + for element in elements do + sb.Append(element.ToString()) |> ignore + + sb.ToString() /// []