From 26d821a11187268bb2333342dc4664ac3e04836e Mon Sep 17 00:00:00 2001 From: Michael Render Date: Mon, 10 Feb 2025 18:35:59 -0500 Subject: [PATCH 1/3] [dotnet] Parse response before deserialization --- dotnet/src/webdriver/Response.cs | 54 +++++++++++++++----------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index afb8b720e2bf0..89989daf09dc2 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -19,10 +19,10 @@ using OpenQA.Selenium.Internal; using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; #nullable enable @@ -30,16 +30,10 @@ namespace OpenQA.Selenium { /// - /// Handles reponses from the browser + /// Handles responses from the browser /// public class Response { - private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() - { - TypeInfoResolver = ResponseJsonSerializerContext.Default, - Converters = { new ResponseValueJsonConverter() } // we still need it to make `Object` as `Dictionary` - }; - /// /// Initializes a new instance of the class /// @@ -82,18 +76,18 @@ public Response(string? sessionId, object? value, WebDriverResult status) /// If is not a valid JSON object. public static Response FromJson(string value) { - Dictionary rawResponse = JsonSerializer.Deserialize>(value, s_jsonSerializerOptions) - ?? throw new WebDriverException("JSON success response returned \"null\" value"); + JsonObject rawResponse = JsonNode.Parse(value) as JsonObject + ?? throw new WebDriverException($"JSON success response did not return a dictionary{Environment.NewLine}{value}"); - object? contents; + JsonNode? contents; string? sessionId = null; - if (rawResponse.TryGetValue("sessionId", out object? s) && s is not null) + if (rawResponse.TryGetPropertyValue("sessionId", out JsonNode? s) && s is not null) { sessionId = s.ToString(); } - if (rawResponse.TryGetValue("value", out object? valueObj)) + if (rawResponse.TryGetPropertyValue("value", out JsonNode? valueObj)) { contents = valueObj; } @@ -107,7 +101,7 @@ public static Response FromJson(string value) // Special-case for the new session command, where the "capabilities" // property of the response is the actual value we're interested in. - if (rawResponse.TryGetValue("capabilities", out object? capabilities)) + if (rawResponse.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) { contents = capabilities; } @@ -117,14 +111,14 @@ public static Response FromJson(string value) } } - if (contents is Dictionary valueDictionary) + if (contents is JsonObject valueDictionary) { // Special case code for the new session command. If the response contains // sessionId and capabilities properties, fix up the session ID and value members. - if (valueDictionary.TryGetValue("sessionId", out object? session)) + if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session)) { sessionId = session.ToString(); - if (valueDictionary.TryGetValue("capabilities", out object? capabilities)) + if (valueDictionary.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) { contents = capabilities; } @@ -135,7 +129,9 @@ public static Response FromJson(string value) } } - return new Response(sessionId, contents, WebDriverResult.Success); + var contentsDictionary = JsonSerializer.Deserialize(contents, ResponseJsonSerializerContext.Default.Object); + + return new Response(sessionId, contentsDictionary, WebDriverResult.Success); } /// @@ -181,29 +177,30 @@ public WebDriverResult Status /// If the JSON dictionary is not in the expected state, per spec. public static Response FromErrorJson(string value) { - Dictionary deserializedResponse = JsonSerializer.Deserialize>(value, s_jsonSerializerOptions) - ?? throw new WebDriverException("JSON error response returned \"null\" value"); + JsonObject responseObject = JsonNode.Parse(value) as JsonObject + ?? throw new WebDriverException($"JSON error response did not return an object{Environment.NewLine}{value}"); - if (!deserializedResponse.TryGetValue("value", out object? valueObject)) + if (!responseObject.TryGetPropertyValue("value", out JsonNode? valueNode)) { - throw new WebDriverException($"The 'value' property was not found in the response:{Environment.NewLine}{value}"); + throw new WebDriverException($"The 'value' property was not found in the response{Environment.NewLine}{value}"); } - if (valueObject is not Dictionary valueDictionary) + if (valueNode is not JsonObject valueObject) { - throw new WebDriverException($"The 'value' property is not a dictionary of {Environment.NewLine}{value}"); + throw new WebDriverException($"The 'value' property is not a dictionary{Environment.NewLine}{value}"); } - if (!valueDictionary.TryGetValue("error", out object? errorObject)) + if (!valueObject.TryGetPropertyValue("error", out JsonNode? errorObject)) { - throw new WebDriverException($"The 'value > error' property was not found in the response:{Environment.NewLine}{value}"); + throw new WebDriverException($"The 'value > error' property was not found in the response{Environment.NewLine}{value}"); } - if (errorObject is not string errorString) + if (errorObject is not JsonValue errorValue || !errorValue.TryGetValue(out string? errorString)) { throw new WebDriverException($"The 'value > error' property is not a string{Environment.NewLine}{value}"); } + var valueDictionary = JsonSerializer.Deserialize(valueObject, ResponseJsonSerializerContext.Default.Object); WebDriverResult status = WebDriverError.ResultFromError(errorString); return new Response(sessionId: null, valueDictionary, status); @@ -243,6 +240,7 @@ public override string ToString() } } - [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(object))] + [JsonSourceGenerationOptions(Converters = [typeof(ResponseValueJsonConverter)])] // we still need it to make `Object` as `Dictionary` internal sealed partial class ResponseJsonSerializerContext : JsonSerializerContext; } From d08708fb94710887f60c3dd4dee0c2469452cf2a Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 26 Feb 2025 15:46:33 -0500 Subject: [PATCH 2/3] Handle if `value.sessionId` is `null` --- dotnet/src/webdriver/Response.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index 8719da77d1494..bf18a6ca9236e 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -95,7 +95,7 @@ public static Response FromJson(string value) { // Special case code for the new session command. If the response contains // sessionId and capabilities properties, fix up the session ID and value members. - if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session)) + if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session) && session is not null) { sessionId = session.ToString(); if (valueDictionary.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) From 2a78f2806edcf46be4ad424f0dca83edc99d0013 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 26 Feb 2025 15:46:55 -0500 Subject: [PATCH 3/3] null check instead --- dotnet/src/webdriver/Response.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index bf18a6ca9236e..8f643e7082809 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -95,9 +95,9 @@ public static Response FromJson(string value) { // Special case code for the new session command. If the response contains // sessionId and capabilities properties, fix up the session ID and value members. - if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session) && session is not null) + if (valueDictionary.TryGetPropertyValue("sessionId", out JsonNode? session)) { - sessionId = session.ToString(); + sessionId = session?.ToString(); if (valueDictionary.TryGetPropertyValue("capabilities", out JsonNode? capabilities)) { contents = capabilities;