diff --git a/src/OpenClaw.SetupEngine/Program.cs b/src/OpenClaw.SetupEngine/Program.cs index 6323defec..95fad937f 100644 --- a/src/OpenClaw.SetupEngine/Program.cs +++ b/src/OpenClaw.SetupEngine/Program.cs @@ -191,7 +191,7 @@ public static async Task Main(string[] args) logPath = config.LogPath, journalPath }; - var json = System.Text.Json.JsonSerializer.Serialize(jsonResult, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); + var json = System.Text.Json.JsonSerializer.Serialize(jsonResult, SetupConfig.JsonWriteOptions); await AtomicFile.WriteAllTextAsync(jsonOutput, json); } diff --git a/src/OpenClaw.SetupEngine/SetupContext.cs b/src/OpenClaw.SetupEngine/SetupContext.cs index 952ddd583..2877673a6 100644 --- a/src/OpenClaw.SetupEngine/SetupContext.cs +++ b/src/OpenClaw.SetupEngine/SetupContext.cs @@ -71,6 +71,15 @@ public SetupConfig ApplyUiDefaults(bool rollbackOnFailure = true) DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, WriteIndented = true }; + + /// + /// Minimal write-only options for producing human-readable JSON files. + /// Shared to avoid repeated heap allocation at call sites. + /// + internal static readonly JsonSerializerOptions JsonWriteOptions = new() + { + WriteIndented = true + }; } // ─── WSL Configuration ─── @@ -220,7 +229,7 @@ public void MergeIntoSettingsFile(string settingsPath) settings.TryAdd(kvp.Key, kvp.Value); Directory.CreateDirectory(Path.GetDirectoryName(settingsPath)!); - var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(settings, SetupConfig.JsonWriteOptions); AtomicFile.WriteAllText(settingsPath, json); } } diff --git a/src/OpenClaw.SetupEngine/SetupSteps.cs b/src/OpenClaw.SetupEngine/SetupSteps.cs index 4f851d16b..da7326cf1 100644 --- a/src/OpenClaw.SetupEngine/SetupSteps.cs +++ b/src/OpenClaw.SetupEngine/SetupSteps.cs @@ -2213,7 +2213,7 @@ private static async Task WriteSetupStateAsync(SetupContext ctx, CancellationTok History = Array.Empty() }; - var json = System.Text.Json.JsonSerializer.Serialize(state, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); + var json = System.Text.Json.JsonSerializer.Serialize(state, SetupConfig.JsonWriteOptions); await AtomicFile.WriteAllTextAsync(statePath, json, ct); ctx.Logger.Info($"Wrote setup-state.json: DistroName={ctx.DistroName}"); } @@ -2286,7 +2286,7 @@ private static void WriteKeepaliveMarker(SetupContext ctx, string markerPath, in StartTimeUtc = DateTimeOffset.UtcNow, ProcessName = "wsl" }; - var json = System.Text.Json.JsonSerializer.Serialize(marker, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); + var json = System.Text.Json.JsonSerializer.Serialize(marker, SetupConfig.JsonWriteOptions); AtomicFile.WriteAllText(markerPath, json); ctx.Logger.Info($"Wrote keepalive marker: {markerPath}"); } diff --git a/src/OpenClaw.SetupEngine/SetupWizardRunner.cs b/src/OpenClaw.SetupEngine/SetupWizardRunner.cs index 0e45820fd..984b13709 100644 --- a/src/OpenClaw.SetupEngine/SetupWizardRunner.cs +++ b/src/OpenClaw.SetupEngine/SetupWizardRunner.cs @@ -279,7 +279,7 @@ private string WriteAnswerTemplate(IReadOnlyList discoveredS Steps = discoveredSteps }; - var json = JsonSerializer.Serialize(template, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(template, SetupConfig.JsonWriteOptions); AtomicFile.WriteAllText(basePath, json); _ctx.Logger.Info($"Wizard answer template written: {basePath}"); return basePath; diff --git a/src/OpenClaw.SetupEngine/TrayArtifactCleanup.cs b/src/OpenClaw.SetupEngine/TrayArtifactCleanup.cs index d96ca78f7..49264b575 100644 --- a/src/OpenClaw.SetupEngine/TrayArtifactCleanup.cs +++ b/src/OpenClaw.SetupEngine/TrayArtifactCleanup.cs @@ -153,8 +153,7 @@ internal static void ResetOnboardingSettings(string appDataDir, SetupLogger logg if (changed) { - var updatedJson = System.Text.Json.JsonSerializer.Serialize(dict, - new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); + var updatedJson = System.Text.Json.JsonSerializer.Serialize(dict, SetupConfig.JsonWriteOptions); AtomicFile.WriteAllText(settingsPath, updatedJson); logger.Info(preserveNodeSettings ? "[Uninstall] Reset onboarding settings (GatewayUrl)" diff --git a/src/OpenClaw.Shared/ChannelConfigPatchBuilder.cs b/src/OpenClaw.Shared/ChannelConfigPatchBuilder.cs index 5cc2e39b4..df88bafe6 100644 --- a/src/OpenClaw.Shared/ChannelConfigPatchBuilder.cs +++ b/src/OpenClaw.Shared/ChannelConfigPatchBuilder.cs @@ -131,7 +131,7 @@ public static ChannelPatchBuildResult BuildPatch( // Reserialize → re-parse → clone so the returned JsonElement isn't // tied to a JsonDocument that goes out of scope on caller side. - var json = JsonSerializer.Serialize(root, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(root, JsonSerializerOptionsCache.WriteIndented); using var doc = JsonDocument.Parse(json); return new ChannelPatchBuildResult { Patch = doc.RootElement.Clone() }; } diff --git a/src/OpenClaw.Shared/DeviceIdentity.cs b/src/OpenClaw.Shared/DeviceIdentity.cs index 3e3d6eddc..0aa943d77 100644 --- a/src/OpenClaw.Shared/DeviceIdentity.cs +++ b/src/OpenClaw.Shared/DeviceIdentity.cs @@ -523,7 +523,7 @@ private void StoreNodeDeviceTokenCore(string token, string[]? scopes) /// private static void AtomicWriteKeyFile(string path, DeviceKeyData data) { - var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); + var json = JsonSerializer.Serialize(data, JsonSerializerOptionsCache.WriteIndented); var dir = Path.GetDirectoryName(path); var tempDir = string.IsNullOrEmpty(dir) ? Environment.CurrentDirectory : dir; var tempPath = Path.Combine(tempDir, $".{Path.GetFileName(path)}.{Guid.NewGuid():N}.tmp"); diff --git a/src/OpenClaw.Shared/JsonSerializerOptionsCache.cs b/src/OpenClaw.Shared/JsonSerializerOptionsCache.cs new file mode 100644 index 000000000..f00329247 --- /dev/null +++ b/src/OpenClaw.Shared/JsonSerializerOptionsCache.cs @@ -0,0 +1,20 @@ +using System.Text.Json; + +namespace OpenClaw.Shared; + +/// +/// Shared, reusable singletons. +/// Prefer these over inline new JsonSerializerOptions { … } allocations +/// at call sites to avoid repeated heap allocation and settings drift. +/// +internal static class JsonSerializerOptionsCache +{ + /// + /// Pretty-print JSON with no additional overrides. + /// Suitable for writing human-readable configuration and diagnostic files. + /// + internal static readonly JsonSerializerOptions WriteIndented = new() + { + WriteIndented = true + }; +} diff --git a/src/OpenClaw.Shared/Models.cs b/src/OpenClaw.Shared/Models.cs index 7223077af..507552cfa 100644 --- a/src/OpenClaw.Shared/Models.cs +++ b/src/OpenClaw.Shared/Models.cs @@ -1789,7 +1789,7 @@ public string DataJson if (_cachedDataJson != null) return _cachedDataJson; try { - _cachedDataJson = JsonSerializer.Serialize(Data, new JsonSerializerOptions { WriteIndented = true }); + _cachedDataJson = JsonSerializer.Serialize(Data, JsonSerializerOptionsCache.WriteIndented); } catch {