From 3783699516d10c4472b6fa3d00de1e13f29f5860 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 09:11:23 +0000 Subject: [PATCH] perf: cache compiled Regex per pattern in JsonSchema.validate JsonSchema.validate previously constructed a brand-new System.Text.RegularExpressions.Regex on every call for a schema with a string 'pattern' constraint. This meant every string value validated against the same schema (e.g. array items, or repeated object properties) recompiled the identical regex from scratch. This change caches compiled Regex instances in a ConcurrentDictionary keyed by pattern string, so the same pattern is only compiled once per process. No behavior change - same match results. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 4 ++++ src/FSharp.Data.Json.Core/JsonSchema.fs | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b4f8565fb..86894494f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,9 @@ # Release Notes +## 8.2.1 - Sep 28 2026 + +- Perf: `JsonSchema.validate` now caches compiled `Regex` instances per `pattern`, instead of recompiling a new `Regex` for every string value validated against a schema `pattern` constraint. No behavior change. + ## 8.2.0 - Jul 06 2026 - Fix: HTML hexadecimal character references (`A`) are now decoded correctly; previously digit-only hex refs were parsed as decimal and refs containing `a`–`f` were left as literal text. diff --git a/src/FSharp.Data.Json.Core/JsonSchema.fs b/src/FSharp.Data.Json.Core/JsonSchema.fs index a25083e50..6f677928e 100644 --- a/src/FSharp.Data.Json.Core/JsonSchema.fs +++ b/src/FSharp.Data.Json.Core/JsonSchema.fs @@ -366,6 +366,14 @@ module JsonSchema = resolve schema + // Cache compiled regexes by pattern so repeated validation of many values against + // the same schema pattern (e.g. array/object properties) doesn't recompile the regex each time. + let private patternRegexCache = + System.Collections.Concurrent.ConcurrentDictionary() + + let private getPatternRegex (pattern: string) = + patternRegexCache.GetOrAdd(pattern, (fun p -> System.Text.RegularExpressions.Regex(p))) + /// Validate a JSON value against a schema let rec validate (schema: JsonSchemaDefinition) (value: JsonValue) : ValidationResult = // Check nulls first @@ -388,7 +396,7 @@ module JsonSchema = // Validate pattern match schema.Pattern with | Some pattern -> - let regex = System.Text.RegularExpressions.Regex(pattern) + let regex = getPatternRegex pattern if regex.IsMatch(str) then Valid