From 9289d273a13dab259e99cafc2f3a48372cf98ff5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 15:59:21 +0000
Subject: [PATCH 1/7] Initial plan
From c8467ca6ebbea7d420cfa0f4fca3cb8e63a42e60 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 16:07:30 +0000
Subject: [PATCH 2/7] Add JSON serialization helper infrastructure for
System.Text.Json migration
Co-authored-by: QilongTang <3942418+QilongTang@users.noreply.github.com>
---
.../Serialization/JsonSerializationHelper.cs | 212 ++++++++++++++++++
1 file changed, 212 insertions(+)
create mode 100644 src/DynamoCore/Serialization/JsonSerializationHelper.cs
diff --git a/src/DynamoCore/Serialization/JsonSerializationHelper.cs b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
new file mode 100644
index 00000000000..8beca39de11
--- /dev/null
+++ b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Dynamo.Serialization
+{
+ ///
+ /// Helper class for JSON serialization using System.Text.Json.
+ /// Provides utilities to replace Newtonsoft.Json functionality.
+ ///
+ public static class JsonSerializationHelper
+ {
+ ///
+ /// Creates default JsonSerializerOptions for Dynamo serialization.
+ ///
+ /// Optional custom converters to include
+ /// Configured JsonSerializerOptions
+ public static JsonSerializerOptions CreateSerializerOptions(params JsonConverter[] converters)
+ {
+ var options = new JsonSerializerOptions
+ {
+ WriteIndented = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.Never,
+ ReferenceHandler = ReferenceHandler.IgnoreCycles,
+ PropertyNameCaseInsensitive = false,
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ // Use invariant culture for consistent serialization
+ Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ };
+
+ // Add custom converters
+ if (converters != null)
+ {
+ foreach (var converter in converters)
+ {
+ options.Converters.Add(converter);
+ }
+ }
+
+ return options;
+ }
+
+ ///
+ /// Creates JsonSerializerOptions for deserialization with backward compatibility.
+ ///
+ /// Optional custom converters to include
+ /// Configured JsonSerializerOptions
+ public static JsonSerializerOptions CreateDeserializerOptions(params JsonConverter[] converters)
+ {
+ var options = new JsonSerializerOptions
+ {
+ WriteIndented = false,
+ DefaultIgnoreCondition = JsonIgnoreCondition.Never,
+ ReferenceHandler = ReferenceHandler.IgnoreCycles,
+ PropertyNameCaseInsensitive = true, // More lenient for reading old files
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip
+ };
+
+ // Add custom converters
+ if (converters != null)
+ {
+ foreach (var converter in converters)
+ {
+ options.Converters.Add(converter);
+ }
+ }
+
+ return options;
+ }
+
+ ///
+ /// Safely gets a string value from a JsonElement.
+ ///
+ public static string GetStringOrDefault(JsonElement element, string defaultValue = "")
+ {
+ return element.ValueKind == JsonValueKind.String ? element.GetString() ?? defaultValue : defaultValue;
+ }
+
+ ///
+ /// Safely gets an int value from a JsonElement.
+ ///
+ public static int GetInt32OrDefault(JsonElement element, int defaultValue = 0)
+ {
+ return element.ValueKind == JsonValueKind.Number && element.TryGetInt32(out var value) ? value : defaultValue;
+ }
+
+ ///
+ /// Safely gets a double value from a JsonElement.
+ ///
+ public static double GetDoubleOrDefault(JsonElement element, double defaultValue = 0.0)
+ {
+ return element.ValueKind == JsonValueKind.Number && element.TryGetDouble(out var value) ? value : defaultValue;
+ }
+
+ ///
+ /// Safely gets a bool value from a JsonElement.
+ ///
+ public static bool GetBooleanOrDefault(JsonElement element, bool defaultValue = false)
+ {
+ if (element.ValueKind == JsonValueKind.True) return true;
+ if (element.ValueKind == JsonValueKind.False) return false;
+ return defaultValue;
+ }
+
+ ///
+ /// Safely gets a Guid value from a JsonElement.
+ ///
+ public static Guid GetGuidOrDefault(JsonElement element, Guid defaultValue = default)
+ {
+ if (element.ValueKind == JsonValueKind.String)
+ {
+ var str = element.GetString();
+ if (Guid.TryParse(str, out var guid))
+ {
+ return guid;
+ }
+ }
+ return defaultValue;
+ }
+
+ ///
+ /// Tries to get a property from a JsonElement.
+ ///
+ public static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement property)
+ {
+ if (element.ValueKind == JsonValueKind.Object)
+ {
+ return element.TryGetProperty(propertyName, out property);
+ }
+ property = default;
+ return false;
+ }
+
+ ///
+ /// Gets an array of JsonElements from a property, or empty array if not found.
+ ///
+ public static JsonElement[] GetArrayOrEmpty(JsonElement element, string propertyName)
+ {
+ if (TryGetProperty(element, propertyName, out var property) && property.ValueKind == JsonValueKind.Array)
+ {
+ var list = new List();
+ foreach (var item in property.EnumerateArray())
+ {
+ list.Add(item);
+ }
+ return list.ToArray();
+ }
+ return Array.Empty();
+ }
+
+ ///
+ /// Deserializes a JsonElement to a specific type using the provided options.
+ ///
+ public static T Deserialize(JsonElement element, JsonSerializerOptions options = null)
+ {
+ var json = element.GetRawText();
+ return JsonSerializer.Deserialize(json, options);
+ }
+
+ ///
+ /// Parses a JSON string and returns a JsonDocument.
+ /// The caller is responsible for disposing the returned JsonDocument.
+ ///
+ public static JsonDocument ParseJson(string json)
+ {
+ return JsonDocument.Parse(json);
+ }
+
+ ///
+ /// Writes a JSON value with error handling.
+ ///
+ public static void WriteValue(Utf8JsonWriter writer, string propertyName, string value)
+ {
+ writer.WriteString(propertyName, value);
+ }
+
+ ///
+ /// Writes a JSON value with error handling.
+ ///
+ public static void WriteValue(Utf8JsonWriter writer, string propertyName, int value)
+ {
+ writer.WriteNumber(propertyName, value);
+ }
+
+ ///
+ /// Writes a JSON value with error handling.
+ ///
+ public static void WriteValue(Utf8JsonWriter writer, string propertyName, double value)
+ {
+ writer.WriteNumber(propertyName, value);
+ }
+
+ ///
+ /// Writes a JSON value with error handling.
+ ///
+ public static void WriteValue(Utf8JsonWriter writer, string propertyName, bool value)
+ {
+ writer.WriteBoolean(propertyName, value);
+ }
+
+ ///
+ /// Writes a JSON value with error handling.
+ ///
+ public static void WriteValue(Utf8JsonWriter writer, string propertyName, Guid value)
+ {
+ writer.WriteString(propertyName, value.ToString());
+ }
+ }
+}
From f4cadad730c22467097b44b233946b410d22f213 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 16:12:16 +0000
Subject: [PATCH 3/7] Migrate low-risk components to System.Text.Json (Phase 1)
Co-authored-by: QilongTang <3942418+QilongTang@users.noreply.github.com>
---
src/DynamoCoreWpf/UI/GuidedTour/CutOffArea.cs | 14 +++----
src/DynamoCoreWpf/UI/GuidedTour/ExitGuide.cs | 12 +++---
src/DynamoCoreWpf/UI/GuidedTour/Guide.cs | 10 ++---
.../UI/GuidedTour/GuidesManager.cs | 4 +-
.../UI/GuidedTour/HightlightArea.cs | 16 ++++----
.../UI/GuidedTour/HostControlInfo.cs | 24 ++++++------
src/DynamoCoreWpf/UI/GuidedTour/Step.cs | 38 +++++++++----------
.../DynamoFeatureFlagsManager.cs | 25 ++++++++++--
.../NotificationCenterController.cs | 6 +--
9 files changed, 84 insertions(+), 65 deletions(-)
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/CutOffArea.cs b/src/DynamoCoreWpf/UI/GuidedTour/CutOffArea.cs
index b236e140859..b7ae53a8fbc 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/CutOffArea.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/CutOffArea.cs
@@ -1,6 +1,6 @@
using System.Windows;
using Dynamo.Core;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Dynamo.Wpf.UI.GuidedTour
{
@@ -20,37 +20,37 @@ public class CutOffArea : NotificationObject
///
/// Since the box that cuts the elements has its size fixed, this variable applies a value to fix its Width
///
- [JsonProperty(nameof(WidthBoxDelta))]
+ [JsonPropertyName(nameof(WidthBoxDelta))]
public double WidthBoxDelta { get => widthBoxDelta; set => widthBoxDelta = value; }
///
/// Since the box that cuts the elements has its size fixed, this variable applies a value to fix its Height
///
- [JsonProperty(nameof(HeightBoxDelta))]
+ [JsonPropertyName(nameof(HeightBoxDelta))]
public double HeightBoxDelta { get => heightBoxDelta; set => heightBoxDelta = value; }
///
/// This property will move the CutOff area horizontally over the X axis
///
- [JsonProperty(nameof(XPosOffset))]
+ [JsonPropertyName(nameof(XPosOffset))]
public double XPosOffset { get => xPosOffset; set => xPosOffset = value; }
///
/// This property will move the CutOff area vertically over the Y axis
///
- [JsonProperty(nameof(YPosOffset))]
+ [JsonPropertyName(nameof(YPosOffset))]
public double YPosOffset { get => yPosOffset; set => yPosOffset = value; }
///
/// In the case the cutoff area is not the same than HostControlInfo.HostUIElementString the this property needs to be populated
///
- [JsonProperty(nameof(WindowElementNameString))]
+ [JsonPropertyName(nameof(WindowElementNameString))]
public string WindowElementNameString { get; set; }
///
/// In cases when we need to put the CutOff area over a node in the Workspace this property will be used
///
- [JsonProperty(nameof(NodeId))]
+ [JsonPropertyName(nameof(NodeId))]
public string NodeId { get; set; }
///
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/ExitGuide.cs b/src/DynamoCoreWpf/UI/GuidedTour/ExitGuide.cs
index 71b89e89ac9..07e3c63e98d 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/ExitGuide.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/ExitGuide.cs
@@ -1,8 +1,8 @@
-using Newtonsoft.Json;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Dynamo.Wpf.UI.GuidedTour
@@ -12,25 +12,25 @@ internal class ExitGuide
///
/// Represents the Height of Exit Guide modal
///
- [JsonProperty("Height")]
+ [JsonPropertyName("Height")]
public double Height { get; set; }
///
/// Represents the Width of Exit Guide modal
///
- [JsonProperty("Width")]
+ [JsonPropertyName("Width")]
public double Width { get; set; }
///
/// Represents the key to the resources related to the Title of Exit Guide modal
///
- [JsonProperty("Title")]
+ [JsonPropertyName("Title")]
public string Title { get; set; }
///
/// Represents the formatted text key to the resources related to the Title of Exit Guide modal
///
- [JsonProperty("FormattedText")]
+ [JsonPropertyName("FormattedText")]
public string FormattedText { get; set; }
}
}
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/Guide.cs b/src/DynamoCoreWpf/UI/GuidedTour/Guide.cs
index 874486afa6b..e8b73bc4c15 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/Guide.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/Guide.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text.Json.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -9,7 +10,6 @@
using Dynamo.ViewModels;
using Dynamo.Wpf.Properties;
using Dynamo.Wpf.Views.GuidedTour;
-using Newtonsoft.Json;
namespace Dynamo.Wpf.UI.GuidedTour
{
@@ -21,13 +21,13 @@ public class Guide
///
/// This list will contain all the steps per guide read from a json file
///
- [JsonProperty("GuideSteps")]
+ [JsonPropertyName("GuideSteps")]
internal List GuideSteps { get; set; }
///
/// This property represent the name of the Guide, e.g. "Get Started", "Packages"
///
- [JsonProperty("Name")]
+ [JsonPropertyName("Name")]
internal string Name { get; set; }
///
@@ -36,13 +36,13 @@ public class Guide
/// 1 - User interface guide
/// 2 - Onboarding guide
///
- [JsonProperty("SequenceOrder")]
+ [JsonPropertyName("SequenceOrder")]
internal int SequenceOrder { get; set; }
///
/// This property has the resource key string for the guide
///
- [JsonProperty("GuideNameResource")]
+ [JsonPropertyName("GuideNameResource")]
internal string GuideNameResource { get; set; }
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs b/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
index deac15a406d..dbcfab1a4f6 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using Dynamo.Controls;
@@ -12,7 +13,6 @@
using Dynamo.Wpf.Properties;
using Dynamo.Wpf.ViewModels.GuidedTour;
using Dynamo.Wpf.Views.GuidedTour;
-using Newtonsoft.Json;
using Res = Dynamo.Wpf.Properties.Resources;
namespace Dynamo.Wpf.UI.GuidedTour
@@ -305,7 +305,7 @@ private static List ReadGuides(string jsonFile)
}
//Deserialize all the information read from the json file
- return JsonConvert.DeserializeObject>(jsonString);
+ return JsonSerializer.Deserialize>(jsonString);
}
///
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/HightlightArea.cs b/src/DynamoCoreWpf/UI/GuidedTour/HightlightArea.cs
index 651a6a546d4..835953bb2a5 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/HightlightArea.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/HightlightArea.cs
@@ -1,6 +1,6 @@
using System.Windows;
using Dynamo.Core;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Dynamo.Wpf.UI.GuidedTour
{
@@ -39,20 +39,20 @@ internal HighlightArea(HighlightArea copyInstance)
///
/// Since the box that highlights the elements has its size fixed, this variable applies a value to fix its Width
///
- [JsonProperty(nameof(WidthBoxDelta))]
+ [JsonPropertyName(nameof(WidthBoxDelta))]
public double WidthBoxDelta { get => widthBoxDelta; set => widthBoxDelta = value; }
///
/// Since the box that highlights the elements has its size fixed, this variable applies a value to fix its Height
///
- [JsonProperty(nameof(HeightBoxDelta))]
+ [JsonPropertyName(nameof(HeightBoxDelta))]
public double HeightBoxDelta { get => heightBoxDelta; set => heightBoxDelta = value; }
///
/// This property will highlight the clickable area if its set to true
///
- [JsonProperty(nameof(HighlightColor))]
+ [JsonPropertyName(nameof(HighlightColor))]
public string HighlightColor { get; set; }
///
@@ -74,26 +74,26 @@ public UIElement WindowElementName
///
/// This property will contain the Window name in case we need to highligh a control in a different Window than DynamoView
///
- [JsonProperty(nameof(WindowName))]
+ [JsonPropertyName(nameof(WindowName))]
public string WindowName { get; set; }
///
/// This represent the UIElement as string of the VisualTree that will be highlighted (readed from the json file)
///
- [JsonProperty(nameof(WindowElementNameString))]
+ [JsonPropertyName(nameof(WindowElementNameString))]
public string WindowElementNameString { get; set; }
///
/// This property represent the UI Element Type that contains the UIElement (WindowElementName) due that in some cases is not the DynamoView (like the sub MenuItems)
///
- [JsonProperty(nameof(UIElementTypeString))]
+ [JsonPropertyName(nameof(UIElementTypeString))]
public string UIElementTypeString { get; set; }
///
/// In case the UIElement to be highlighted is in a Grid, then this property will have the Grid name
///
- [JsonProperty(nameof(UIElementGridContainer))]
+ [JsonPropertyName(nameof(UIElementGridContainer))]
public string UIElementGridContainer { get; set; }
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/HostControlInfo.cs b/src/DynamoCoreWpf/UI/GuidedTour/HostControlInfo.cs
index 9661d2a710b..59ebab9aca5 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/HostControlInfo.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/HostControlInfo.cs
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls.Primitives;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Dynamo.Wpf.UI.GuidedTour
{
@@ -71,19 +71,19 @@ public UIElement HostUIElement
///
/// This variable will hold the name of the host (UIElement) in a string representation
///
- [JsonProperty("HostUIElementString")]
+ [JsonPropertyName("HostUIElementString")]
public string HostUIElementString { get; set; }
///
/// This property will hold the placement location of the popup, for now we are just using Right, Left, Top and Bottom
///
- [JsonProperty("PopupPlacement")]
+ [JsonPropertyName("PopupPlacement")]
public PlacementMode PopupPlacement { get; set; }
///
/// Once the popup host control and placecement is set we can use this property for moving the popup location Vertically (by specifying an offset)
///
- [JsonProperty("VerticalPopupOffset")]
+ [JsonPropertyName("VerticalPopupOffset")]
public double VerticalPopupOffSet
{
get
@@ -99,7 +99,7 @@ public double VerticalPopupOffSet
///
/// Once the popup host control and placecement is set we can use this property for moving the popup location Horizontally (by specifying an offset)
///
- [JsonProperty("HorizontalPopupOffset")]
+ [JsonPropertyName("HorizontalPopupOffset")]
public double HorizontalPopupOffSet
{
get
@@ -115,31 +115,31 @@ public double HorizontalPopupOffSet
///
/// Property that represents the highlight rectangle that will be shown over the Overlay
///
- [JsonProperty("HighlightRectArea")]
+ [JsonPropertyName("HighlightRectArea")]
internal HighlightArea HighlightRectArea { get; set; }
///
/// Property that represents the cut off section that will be removed from the Overlay
///
- [JsonProperty("CutOffRectArea")]
+ [JsonPropertyName("CutOffRectArea")]
internal CutOffArea CutOffRectArea { get; set; }
///
/// The html page that is going to be rendered inside the popup
///
- [JsonProperty("HtmlPage")]
+ [JsonPropertyName("HtmlPage")]
public HtmlPage HtmlPage { get => htmlPage; set => htmlPage = value; }
///
/// This property will contain the WPF Window Name for the cases when the Popup need to be located in a different Window than DynamoView
///
- [JsonProperty(nameof(WindowName))]
+ [JsonPropertyName(nameof(WindowName))]
internal string WindowName { get; set; }
///
/// This property will decide if the Popup.PlacementTarget needs to be calculated again or not (probably after UI Automation a new Window was opened)
///
- [JsonProperty(nameof(DynamicHostWindow))]
+ [JsonPropertyName(nameof(DynamicHostWindow))]
internal bool DynamicHostWindow { get; set; }
}
@@ -151,13 +151,13 @@ public class HtmlPage
///
/// A dictionary containing the key word to be replaced in page and the filename as values
///
- [JsonProperty("Resources")]
+ [JsonPropertyName("Resources")]
public Dictionary Resources { get => resources; set => resources = value; }
///
/// Filename of the HTML page
///
- [JsonProperty("FileName")]
+ [JsonPropertyName("FileName")]
public string FileName { get => fileName; set => fileName = value; }
}
}
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/Step.cs b/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
index 29699acd26b..01fbbd1c16a 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
@@ -16,7 +16,7 @@
using Dynamo.ViewModels;
using Dynamo.Wpf.Properties;
using Dynamo.Wpf.Views.GuidedTour;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
namespace Dynamo.Wpf.UI.GuidedTour
{
@@ -57,73 +57,73 @@ public enum StepTypes{ SURVEY, TOOLTIP, WELCOME, EXIT_TOUR};
///
/// The step type will describe which type of window will be created after reading the json file, it can be SURVEY, TOOLTIP, WELCOME, EXIT_TOUR
///
- [JsonProperty("Type")]
+ [JsonPropertyName("Type")]
public StepTypes StepType { get; set; }
///
/// The step content will contain the title and the popup content (included formatted text)
///
- [JsonProperty("StepContent")]
+ [JsonPropertyName("StepContent")]
public Content StepContent { get; set; }
///
/// There are some specific Steps that will contain extra content (like Survey.RatingTextTitle), then this list will store the information
///
- [JsonProperty("ExtraContent")]
+ [JsonPropertyName("ExtraContent")]
public List StepExtraContent { get; set; }
///
/// Step name, it just represent a step identifier
///
- [JsonProperty("Name")]
+ [JsonPropertyName("Name")]
public string Name { get; set; }
///
/// When the Step Width property is not provided the default value will be 480
///
- [JsonProperty("Width")]
+ [JsonPropertyName("Width")]
public double Width { get; set; } = 480;
///
/// When the Step Height property is not provide the default value will be 190
///
- [JsonProperty("Height")]
+ [JsonPropertyName("Height")]
public double Height { get; set; } = 190;
///
/// Represent a sequencial numeric value for each step, when is a multiflow guide this value can be repeated
///
- [JsonProperty("Sequence")]
+ [JsonPropertyName("Sequence")]
public int Sequence { get; set; } = 0;
///
/// This property contains the Host information like the host popup element or the popup position
///
- [JsonProperty("HostPopupInfo")]
+ [JsonPropertyName("HostPopupInfo")]
public HostControlInfo HostPopupInfo { get; set; }
///
/// This property will hold a list of UI Automation actions (information) that will be executed when the Next or Back button are pressed
///
- [JsonProperty("UIAutomation")]
+ [JsonPropertyName("UIAutomation")]
public List UIAutomation { get; set; }
///
/// This property will hold information about the methods/actions that should be executed before showing a Popup(Step)
///
- [JsonProperty("PreValidation")]
+ [JsonPropertyName("PreValidation")]
internal PreValidation PreValidationInfo { get; set; }
///
/// This property will show the library if It's set to true
///
- [JsonProperty("ShowLibrary")]
+ [JsonPropertyName("ShowLibrary")]
public bool ShowLibrary { get; set; }
///
/// This propertu will hold information about the exit guide modal
///
- [JsonProperty("ExitGuide")]
+ [JsonPropertyName("ExitGuide")]
internal ExitGuide ExitGuide { get; set; }
public enum PointerDirection { TOP_RIGHT, TOP_LEFT, BOTTOM_RIGHT, BOTTOM_LEFT, BOTTOM_DOWN, NONE };
@@ -186,12 +186,12 @@ internal Popup StepUIPopup
/// This property describe which will be the pointing direction of the tooltip (if is a Welcome or Survey popup then is not used)
/// By default the tooltip pointer will be pointing to the left and will be located at the top
///
- [JsonProperty("TooltipPointerDirection")]
+ [JsonPropertyName("TooltipPointerDirection")]
public PointerDirection TooltipPointerDirection { get; set; } = PointerDirection.TOP_LEFT;
///
/// A vertical offfset to the pointer of the popups
///
- [JsonProperty("PointerVerticalOffset")]
+ [JsonPropertyName("PointerVerticalOffset")]
public double PointerVerticalOffset { get; set; }
#endregion
@@ -868,13 +868,13 @@ public class Content
///
/// Title of the Popup shown at the top-center
///
- [JsonProperty("Title")]
+ [JsonPropertyName("Title")]
public string Title { get; set; }
///
/// The content of the Popup using a specific format for showing text, hyperlinks,images, bullet points in a RichTextBox
///
- [JsonProperty("FormattedText")]
+ [JsonPropertyName("FormattedText")]
public string FormattedText
{
get
@@ -896,10 +896,10 @@ public string FormattedText
///
public class ExtraContent
{
- [JsonProperty("Property")]
+ [JsonPropertyName("Property")]
public string Property { get; set; }
- [JsonProperty("Value")]
+ [JsonPropertyName("Value")]
public string Value { get; set; }
}
}
diff --git a/src/DynamoUtilities/DynamoFeatureFlagsManager.cs b/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
index 40765e493d0..9a00c7eef26 100644
--- a/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
+++ b/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
@@ -1,11 +1,11 @@
using Dynamo.Utilities;
-using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -68,14 +68,33 @@ internal void CacheAllFlags()
//convert from json string to dictionary.
try
{
- AllFlagsCache = JsonConvert.DeserializeObject>(dataFromCLI);
+ // System.Text.Json deserializes numbers as JsonElement by default for Dictionary
+ // We need to convert them to the appropriate types
+ var rawCache = JsonSerializer.Deserialize>(dataFromCLI);
+ AllFlagsCache = new Dictionary();
+
+ foreach (var kvp in rawCache)
+ {
+ var element = kvp.Value;
+ object value = element.ValueKind switch
+ {
+ JsonValueKind.String => element.GetString(),
+ JsonValueKind.Number => element.TryGetInt64(out var longVal) ? longVal : element.GetDouble(),
+ JsonValueKind.True => true,
+ JsonValueKind.False => false,
+ JsonValueKind.Null => null,
+ _ => element.ToString()
+ };
+ AllFlagsCache[kvp.Key] = value;
+ }
+
// Invoke the flags retrieved event on the sync context which should be the main ui thread (if in Dynamo with UI) or the default SyncContext (if in headless mode).
syncContext?.Send((_) =>
{
FlagsRetrieved?.Invoke();
}, null);
- var formattedFlags = JsonConvert.SerializeObject(AllFlagsCache, Formatting.Indented);
+ var formattedFlags = JsonSerializer.Serialize(AllFlagsCache, new JsonSerializerOptions { WriteIndented = true });
OnLogMessage($"retrieved feature flags with value: {formattedFlags}");
}
diff --git a/src/Notifications/NotificationCenterController.cs b/src/Notifications/NotificationCenterController.cs
index 312012a851e..4aa6393d8ce 100755
--- a/src/Notifications/NotificationCenterController.cs
+++ b/src/Notifications/NotificationCenterController.cs
@@ -6,6 +6,7 @@
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
@@ -15,7 +16,6 @@
using DynamoUtilities;
using Dynamo.ViewModels;
using Dynamo.Wpf.ViewModels.Core;
-using Newtonsoft.Json;
using Microsoft.Web.WebView2.Wpf;
using Dynamo.Utilities;
using Dynamo.Configuration;
@@ -147,7 +147,7 @@ private void WebView_NavigationCompleted(object sender, Microsoft.Web.WebView2.C
private void AddNotifications(List notifications)
{
- var notificationsList = JsonConvert.SerializeObject(notifications);
+ var notificationsList = JsonSerializer.Serialize(notifications);
InvokeJS($"window.setNotifications({notificationsList});");
}
@@ -163,7 +163,7 @@ private void RequestNotifications()
using (StreamReader reader = new StreamReader(stream))
{
jsonStringFile = reader.ReadToEnd();
- notificationsModel = JsonConvert.DeserializeObject(jsonStringFile);
+ notificationsModel = JsonSerializer.Deserialize(jsonStringFile);
//We are adding a limit of months to grab the notifications
var limitDate = DateTime.Now.AddMonths(-limitOfMonthsFilterNotifications);
From e33d4b2baa0e19c88b5cc396c4611d87b7389db6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 16:13:38 +0000
Subject: [PATCH 4/7] Remove unused Newtonsoft.Json usings from chart node
models
Co-authored-by: QilongTang <3942418+QilongTang@users.noreply.github.com>
---
src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs | 1 -
.../CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs | 1 -
src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs | 1 -
src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs | 1 -
src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs | 1 -
src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs | 1 -
6 files changed, 6 deletions(-)
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
index 24b6137b7ae..71c1f50df5e 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
@@ -16,7 +16,6 @@
using Dynamo.Wpf;
using Dynamo.Wpf.Properties;
using LiveChartsCore.SkiaSharpView.Painting;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using SkiaSharp.Views.WPF;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
index cbdd25dbc45..00b2b6f497e 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
@@ -13,7 +13,6 @@
using Dynamo.Controls;
using Dynamo.Graph.Nodes;
using Dynamo.Wpf;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
index 821f54a676f..97c7540e98c 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
@@ -14,7 +14,6 @@
using Dynamo.Controls;
using Dynamo.Graph.Nodes;
using Dynamo.Wpf;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
index 5d364ab51c4..93172b3820e 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
@@ -14,7 +14,6 @@
using Dynamo.Controls;
using Dynamo.Graph.Nodes;
using Dynamo.Wpf;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
index 483c20d0371..d3b2a6615ed 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
@@ -14,7 +14,6 @@
using Dynamo.Controls;
using Dynamo.Graph.Nodes;
using Dynamo.Wpf;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
index fcb8fc8bf54..8e1d0392751 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
@@ -16,7 +16,6 @@
using Dynamo.Graph.Nodes;
using Dynamo.ViewModels;
using Dynamo.Wpf;
-using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
From bee2b54f654069ada74e06b5c5ecf78285137f71 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 16:15:47 +0000
Subject: [PATCH 5/7] Address code review feedback: Add JsonPropertyName to
NotificationsModel, fix security and documentation issues
Co-authored-by: QilongTang <3942418+QilongTang@users.noreply.github.com>
---
.../Serialization/JsonSerializationHelper.cs | 13 +++++++++++--
src/DynamoCoreWpf/UI/GuidedTour/Step.cs | 2 +-
src/Notifications/NotificationsModel.cs | 18 ++++++++++++++++++
3 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/src/DynamoCore/Serialization/JsonSerializationHelper.cs b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
index 8beca39de11..b88c486c030 100644
--- a/src/DynamoCore/Serialization/JsonSerializationHelper.cs
+++ b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
@@ -26,8 +26,8 @@ public static JsonSerializerOptions CreateSerializerOptions(params JsonConverter
PropertyNameCaseInsensitive = false,
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
- // Use invariant culture for consistent serialization
- Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+ // Note: Using default encoder for security. If special characters need to be unescaped,
+ // evaluate security implications before changing to UnsafeRelaxedJsonEscaping.
};
// Add custom converters
@@ -164,8 +164,17 @@ public static T Deserialize(JsonElement element, JsonSerializerOptions option
/// Parses a JSON string and returns a JsonDocument.
/// The caller is responsible for disposing the returned JsonDocument.
///
+ /// The JSON string to parse
+ /// A JsonDocument representing the parsed JSON
+ /// Thrown when the JSON is malformed
+ /// Thrown when json parameter is null or empty
public static JsonDocument ParseJson(string json)
{
+ if (string.IsNullOrEmpty(json))
+ {
+ throw new ArgumentException("JSON string cannot be null or empty", nameof(json));
+ }
+
return JsonDocument.Parse(json);
}
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/Step.cs b/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
index 01fbbd1c16a..ed182ab1be1 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/Step.cs
@@ -121,7 +121,7 @@ public enum StepTypes{ SURVEY, TOOLTIP, WELCOME, EXIT_TOUR};
public bool ShowLibrary { get; set; }
///
- /// This propertu will hold information about the exit guide modal
+ /// This property will hold information about the exit guide modal
///
[JsonPropertyName("ExitGuide")]
internal ExitGuide ExitGuide { get; set; }
diff --git a/src/Notifications/NotificationsModel.cs b/src/Notifications/NotificationsModel.cs
index f13f80a394f..62491807e62 100644
--- a/src/Notifications/NotificationsModel.cs
+++ b/src/Notifications/NotificationsModel.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
+using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Dynamo.Notifications
@@ -11,12 +12,15 @@ namespace Dynamo.Notifications
internal class NotificationsModel
{
[DataMember(Name = "version")]
+ [JsonPropertyName("version")]
internal string Version { get; set; }
[DataMember(Name = "last_update_timestamp")]
+ [JsonPropertyName("last_update_timestamp")]
internal DateTime LastUpdate { get; set; }
[DataMember(Name = "notifications")]
+ [JsonPropertyName("notifications")]
internal List Notifications { get; set; }
}
@@ -24,45 +28,59 @@ internal class NotificationsModel
internal class NotificationItemModel
{
[DataMember(Name = "id")]
+ [JsonPropertyName("id")]
internal string Id { get; set; }
[DataMember(Name = "title")]
+ [JsonPropertyName("title")]
internal string Title { get; set; }
[DataMember(Name = "link")]
+ [JsonPropertyName("link")]
internal string Link { get; set; }
[DataMember(Name = "linkTitle")]
+ [JsonPropertyName("linkTitle")]
internal string LinkTitle { get; set; }
[DataMember(Name = "longDescription")]
+ [JsonPropertyName("longDescription")]
internal string LongDescription { get; set; }
[DataMember(Name = "created")]
+ [JsonPropertyName("created")]
internal DateTime Created { get; set; }
[DataMember(Name = "thumbnail")]
+ [JsonPropertyName("thumbnail")]
internal string Thumbnail { get; set; }
[DataMember(Name = "priority")]
+ [JsonPropertyName("priority")]
internal string Priority { get; set; }
[DataMember(Name = "type")]
+ [JsonPropertyName("type")]
internal string Type { get; set; }
[DataMember(Name = "scope")]
+ [JsonPropertyName("scope")]
internal string Scope { get; set; }
[DataMember(Name = "source")]
+ [JsonPropertyName("source")]
internal string Source { get; set; }
[DataMember(Name = "status")]
+ [JsonPropertyName("status")]
internal string Status { get; set; }
[DataMember(Name = "isPinned")]
+ [JsonPropertyName("isPinned")]
internal bool IsPinned { get; set; }
[DataMember(Name = "isRead")]
+ [JsonPropertyName("isRead")]
internal bool IsRead { get; set; } = true;
}
}
From 6c0b470d11d3bf60798104b43b89fffed65fac5e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Jan 2026 22:54:17 +0000
Subject: [PATCH 6/7] Restore Newtonsoft.Json using statements in chart files
that use JsonIgnore attribute
Co-authored-by: QilongTang <3942418+QilongTang@users.noreply.github.com>
---
src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs | 1 +
.../CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs | 1 +
src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs | 1 +
src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs | 1 +
src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs | 1 +
src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs | 1 +
6 files changed, 6 insertions(+)
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
index 71c1f50df5e..24b6137b7ae 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/BarChartNodeModel.cs
@@ -16,6 +16,7 @@
using Dynamo.Wpf;
using Dynamo.Wpf.Properties;
using LiveChartsCore.SkiaSharpView.Painting;
+using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using SkiaSharp.Views.WPF;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
index 00b2b6f497e..cbdd25dbc45 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/BasicLineChartNodeModel.cs
@@ -13,6 +13,7 @@
using Dynamo.Controls;
using Dynamo.Graph.Nodes;
using Dynamo.Wpf;
+using Newtonsoft.Json;
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
index 97c7540e98c..3b19b08b0aa 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/HeatSeriesNodeModel.cs
@@ -18,6 +18,7 @@
using DynamoServices;
using Dynamo.Wpf.Properties;
using Dynamo.Graph.Connectors;
+using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Dynamo.ViewModels;
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
index 93172b3820e..e403ce009df 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/PieChartNodeModel.cs
@@ -19,6 +19,7 @@
using Dynamo.Wpf.Properties;
using Xceed.Wpf.Toolkit;
using Dynamo.Graph.Connectors;
+using Newtonsoft.Json;
using Dynamo.ViewModels;
namespace CoreNodeModelsWpf.Charts
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
index d3b2a6615ed..689e7c12f90 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/ScatterPlotNodeModel.cs
@@ -19,6 +19,7 @@
using Dynamo.Wpf.Properties;
using ProtoCore.DesignScriptParser;
using Dynamo.Graph.Connectors;
+using Newtonsoft.Json;
using Dynamo.ViewModels;
namespace CoreNodeModelsWpf.Charts
diff --git a/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs b/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
index 8e1d0392751..cc64daf8901 100644
--- a/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
+++ b/src/Libraries/CoreNodeModelsWpf/Charts/XYLineChartNodeModel.cs
@@ -19,6 +19,7 @@
using ProtoCore.AST.AssociativeAST;
using DynamoServices;
using Dynamo.Wpf.Properties;
+using Newtonsoft.Json;
namespace CoreNodeModelsWpf.Charts
{
From 66b1305a269b86eb17d1233bf426fa8227723030 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 27 Jan 2026 23:46:30 +0000
Subject: [PATCH 7/7] Fix deserialization issues: add null checks and
case-insensitive options for JSON parsing
Co-authored-by: zeusongit <32665108+zeusongit@users.noreply.github.com>
---
.../Serialization/JsonSerializationHelper.cs | 3 +++
src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs | 10 +++++++++-
src/DynamoUtilities/DynamoFeatureFlagsManager.cs | 8 ++++++++
src/Notifications/NotificationCenterController.cs | 10 +++++++++-
4 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/src/DynamoCore/Serialization/JsonSerializationHelper.cs b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
index b88c486c030..61d23cb6e6d 100644
--- a/src/DynamoCore/Serialization/JsonSerializationHelper.cs
+++ b/src/DynamoCore/Serialization/JsonSerializationHelper.cs
@@ -154,6 +154,9 @@ public static JsonElement[] GetArrayOrEmpty(JsonElement element, string property
///
/// Deserializes a JsonElement to a specific type using the provided options.
///
+ /// The JsonElement to deserialize
+ /// Optional serializer options
+ /// The deserialized object, which may be null for reference types
public static T Deserialize(JsonElement element, JsonSerializerOptions options = null)
{
var json = element.GetRawText();
diff --git a/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs b/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
index dbcfab1a4f6..a37d72905ec 100644
--- a/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
+++ b/src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
@@ -304,8 +304,16 @@ private static List ReadGuides(string jsonFile)
jsonString = r.ReadToEnd();
}
+ // Use case-insensitive deserialization to match JSON property names
+ var options = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true,
+ AllowTrailingCommas = true,
+ ReadCommentHandling = JsonCommentHandling.Skip
+ };
+
//Deserialize all the information read from the json file
- return JsonSerializer.Deserialize>(jsonString);
+ return JsonSerializer.Deserialize>(jsonString, options);
}
///
diff --git a/src/DynamoUtilities/DynamoFeatureFlagsManager.cs b/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
index 9a00c7eef26..6fe3938e5d1 100644
--- a/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
+++ b/src/DynamoUtilities/DynamoFeatureFlagsManager.cs
@@ -71,6 +71,14 @@ internal void CacheAllFlags()
// System.Text.Json deserializes numbers as JsonElement by default for Dictionary
// We need to convert them to the appropriate types
var rawCache = JsonSerializer.Deserialize>(dataFromCLI);
+
+ if (rawCache == null)
+ {
+ // No flags could be deserialized; keep cache empty and return.
+ RaiseMessageLogged("Failed to deserialize feature flags - received null result");
+ return;
+ }
+
AllFlagsCache = new Dictionary();
foreach (var kvp in rawCache)
diff --git a/src/Notifications/NotificationCenterController.cs b/src/Notifications/NotificationCenterController.cs
index 4aa6393d8ce..87ba0c7a08b 100755
--- a/src/Notifications/NotificationCenterController.cs
+++ b/src/Notifications/NotificationCenterController.cs
@@ -163,7 +163,15 @@ private void RequestNotifications()
using (StreamReader reader = new StreamReader(stream))
{
jsonStringFile = reader.ReadToEnd();
- notificationsModel = JsonSerializer.Deserialize(jsonStringFile);
+
+ // Use case-insensitive deserialization to match JSON property names
+ var options = new JsonSerializerOptions
+ {
+ PropertyNameCaseInsensitive = true,
+ AllowTrailingCommas = true
+ };
+
+ notificationsModel = JsonSerializer.Deserialize(jsonStringFile, options);
//We are adding a limit of months to grab the notifications
var limitDate = DateTime.Now.AddMonths(-limitOfMonthsFilterNotifications);