You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Moving polymorphic serialization discriminator for LocalValue abstract type into derived types, which is explicitly null for RemoteReference types.
🔄 Types of changes
Bug fix (backwards compatible)
New feature (non-breaking change which adds functionality and tests!)
PR Type
Bug fix, Enhancement
Description
Removed polymorphic serialization from LocalValue abstract type
Added RemoteReferenceLocalValue abstract type with implementations
Added explicit Type properties to all LocalValue derived classes
Refactored serialization to use individual type discriminators
Diagram Walkthrough
flowchart LR
A["LocalValue<br/>abstract record"] --> B["RemoteReferenceLocalValue<br/>abstract record"]
A --> C["PrimitiveProtocolLocalValue<br/>abstract record"]
A --> D["ChannelLocalValue<br/>sealed record"]
A --> E["ArrayLocalValue<br/>sealed record"]
B --> F["SharedReferenceLocalValue<br/>sealed record"]
B --> G["RemoteObjectReferenceLocalValue<br/>sealed record"]
C --> H["NumberLocalValue<br/>with Type property"]
C --> I["StringLocalValue<br/>with Type property"]
J["Removed<br/>JsonPolymorphic"] -.-> K["Added<br/>Type properties"]
Loading
File Walkthrough
Relevant files
Enhancement
LocalValue.cs
Refactor LocalValue serialization and add RemoteReference types
dotnet/src/webdriver/BiDi/Script/LocalValue.cs
Removed JsonPolymorphic attribute and all JsonDerivedType declarations from LocalValue base class
Added new RemoteReferenceLocalValue abstract record with two sealed implementations: SharedReferenceLocalValue and RemoteObjectReferenceLocalValue
Added explicit Type property with [JsonInclude] attribute to all 16 derived LocalValue classes for individual type discrimination
Reorganized class hierarchy to include new RemoteReferenceLocalValue branch alongside existing PrimitiveProtocolLocalValue and other types
nvborisenko
changed the title
[dotnet] [bidi] Added missing Script.RemoteReference LocaclValue type
[dotnet] [bidi] Added missing Script.RemoteReference LocalValue type
Dec 11, 2025
qodo-code-reviewbot
changed the title
[dotnet] [bidi] Added missing Script.RemoteReference LocalValue type
[dotnet] [bidi] Added missing Script.RemoteReference LocaclValue type
Dec 11, 2025
Objective: To create a detailed and reliable record of critical system actions for security analysis and compliance.
Status: No auditing scope: The changes add/modify type models and serialization details without introducing or touching any critical actions or logging, so there is no evidence of missing required audit trails in this diff but also nothing to assess audit coverage.
Referred Code
usingOpenQA.Selenium.BiDi.Json.Converters;usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Numerics;usingSystem.Text.Json.Serialization;usingSystem.Text.RegularExpressions;namespaceOpenQA.Selenium.BiDi.Script;publicabstractrecordLocalValue{publicstaticimplicitoperatorLocalValue(bool?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(int?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(double?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(string?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(DateTimeOffset?value){returnConvertFrom(value);}// TODO: Extend converting from typespublicstaticLocalValueConvertFrom(object?value)
...(clipped 322 lines)
Generic: Robust Error Handling and Edge Case Management
Objective: Ensure comprehensive error handling that provides meaningful context and graceful degradation
Status: No error paths: The diff introduces/updates data records and type discriminators without adding operations that can fail, so there are no new failure points to assess; robustness appears unchanged but cannot be fully verified from this snippet.
Generic: Security-First Input Validation and Data Handling
Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent vulnerabilities
Status: Serialization surface: The added explicit type discriminators and new records change the serialization surface but the diff does not show external input validation or security-sensitive handling, so security impact cannot be fully assessed from this isolated model code.
✅ Add missing JSON type propertySuggestion Impact:The commit added an abstract Type property to LocalValue with [JsonInclude], and implemented it in all subclasses. For RemoteReferenceLocalValue’s derived classes, Type is now overridden (though set to null! here), addressing the missing Type property for remote references and aligning with the serialization pattern.
code diff:
+ [JsonInclude]+ internal abstract string Type { get; }
}
public abstract record RemoteReferenceLocalValue : LocalValue, IRemoteReference;
@@ -272,91 +291,82 @@
public sealed record SharedReferenceLocalValue(string SharedId) : RemoteReferenceLocalValue, ISharedReference
{
public Handle? Handle { get; set; }
++ internal override string Type { get; } = null!;
}
public sealed record RemoteObjectReferenceLocalValue(Handle Handle) : RemoteReferenceLocalValue, IRemoteObjectReference
{
public string? SharedId { get; set; }
++ internal override string Type { get; } = null!;
}
Add the missing Type property to the RemoteReferenceLocalValue abstract record to ensure correct JSON serialization for its derived classes.
-public abstract record RemoteReferenceLocalValue : LocalValue, IRemoteReference;+public abstract record RemoteReferenceLocalValue : LocalValue, IRemoteReference+{+ [JsonInclude]+ internal string Type { get; } = "remote";+}
public sealed record SharedReferenceLocalValue(string SharedId) : RemoteReferenceLocalValue, ISharedReference
{
public Handle? Handle { get; set; }
}
public sealed record RemoteObjectReferenceLocalValue(Handle Handle) : RemoteReferenceLocalValue, IRemoteObjectReference
{
public string? SharedId { get; set; }
}
[Suggestion processed]
Suggestion importance[1-10]: 9
__
Why: The suggestion correctly identifies that the new RemoteReferenceLocalValue and its derived classes are missing the Type property, which is essential for correct JSON serialization and is inconsistent with the pattern applied to all other types in this PR.
High
High-level
Re-evaluate the serialization strategy change
Reconsider replacing the standard JsonPolymorphic attribute with manual Type properties in each subclass. This change increases complexity and likely requires a custom deserializer, so reverting to the built-in polymorphism support is recommended unless there's a strong reason against it.
publicabstractrecordLocalValue{publicstaticimplicitoperatorLocalValue(bool?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(int?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(double?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(string?value){returnConvertFrom(value);}publicstaticimplicitoperatorLocalValue(DateTimeOffset?value){returnConvertFrom(value);}// TODO: Extend converting from typespublicstaticLocalValueConvertFrom(object?value)
...(clipped 5 lines)
Solution Walkthrough:
Before:
// No JsonPolymorphic attributepublicabstractrecordLocalValue{// ...}publicsealedrecordNumberLocalValue(...):PrimitiveProtocolLocalValue{[JsonInclude]internalstringType{get;}="number";}publicsealedrecordStringLocalValue(...):PrimitiveProtocolLocalValue{[JsonInclude]internalstringType{get;}="string";}// ... and so on for all derived types
After:
[JsonPolymorphic(TypeDiscriminatorPropertyName="type")][JsonDerivedType(typeof(NumberLocalValue),"number")][JsonDerivedType(typeof(StringLocalValue),"string")]// ... other existing types[JsonDerivedType(typeof(SharedReferenceLocalValue),"sharedId")]// Example type name[JsonDerivedType(typeof(RemoteObjectReferenceLocalValue),"handle")]// Example type namepublicabstractrecordLocalValue{// ...}publicsealedrecordNumberLocalValue(...):PrimitiveProtocolLocalValue;publicsealedrecordStringLocalValue(...):PrimitiveProtocolLocalValue;// ... derived types do not need an explicit 'Type' property
Suggestion importance[1-10]: 8
__
Why: This is a significant architectural suggestion that correctly questions the removal of the standard JsonPolymorphic attribute in favor of a more complex and manual approach, which increases maintenance overhead.
Medium
General
Use init-only properties for consistency
Change the mutable properties Handle and SharedId in SharedReferenceLocalValue and RemoteObjectReferenceLocalValue to be init-only for consistency with record type immutability.
public sealed record SharedReferenceLocalValue(string SharedId) : RemoteReferenceLocalValue, ISharedReference
{
- public Handle? Handle { get; set; }+ public Handle? Handle { get; init; }
}
public sealed record RemoteObjectReferenceLocalValue(Handle Handle) : RemoteReferenceLocalValue, IRemoteObjectReference
{
- public string? SharedId { get; set; }+ public string? SharedId { get; init; }
}
Apply / Chat
Suggestion importance[1-10]: 6
__
Why: The suggestion correctly points out an inconsistency in property mutability and proposes using init setters to align with the immutable nature of record types, which improves code quality and maintainability.
Low
Learned best practice
✅ Centralize type discriminator logicSuggestion Impact:The commit moved the Type property to an abstract member on the base LocalValue and overrode it in each derived record, eliminating repeated [JsonInclude] properties per record. While it did not introduce the exact TypedLocalValue/TypedPrimitiveLocalValue helper types suggested, it centralized the discriminator logic as proposed.
code diff:
@@ -265,6 +281,9 @@
return new ObjectLocalValue(values);
}
++ [JsonInclude]+ internal abstract string Type { get; }
}
public abstract record RemoteReferenceLocalValue : LocalValue, IRemoteReference;
@@ -272,91 +291,82 @@
public sealed record SharedReferenceLocalValue(string SharedId) : RemoteReferenceLocalValue, ISharedReference
{
public Handle? Handle { get; set; }
++ internal override string Type { get; } = null!;
}
public sealed record RemoteObjectReferenceLocalValue(Handle Handle) : RemoteReferenceLocalValue, IRemoteObjectReference
{
public string? SharedId { get; set; }
++ internal override string Type { get; } = null!;
}
public abstract record PrimitiveProtocolLocalValue : LocalValue;
public sealed record NumberLocalValue([property: JsonConverter(typeof(SpecialNumberConverter))] double Value) : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "number";+ internal override string Type { get; } = "number";
public static explicit operator NumberLocalValue(double n) => new NumberLocalValue(n);
}
public sealed record StringLocalValue(string Value) : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "string";+ internal override string Type { get; } = "string";
}
public sealed record NullLocalValue : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "null";+ internal override string Type { get; } = "null";
}
public sealed record UndefinedLocalValue : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "undefined";+ internal override string Type { get; } = "undefined";
}
public sealed record BooleanLocalValue(bool Value) : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "boolean";+ internal override string Type { get; } = "boolean";
}
public sealed record BigIntLocalValue(string Value) : PrimitiveProtocolLocalValue
{
- [JsonInclude]- internal string Type { get; } = "bigint";+ internal override string Type { get; } = "bigint";
}
public sealed record ChannelLocalValue(ChannelProperties Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "channel";+ internal override string Type { get; } = "channel";
}
public sealed record ArrayLocalValue(IEnumerable<LocalValue> Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "array";+ internal override string Type { get; } = "array";
}
public sealed record DateLocalValue(string Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "date";+ internal override string Type { get; } = "date";
}
public sealed record MapLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "map";+ internal override string Type { get; } = "map";
}
public sealed record ObjectLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "object";+ internal override string Type { get; } = "object";
}
public sealed record RegExpLocalValue(RegExpValue Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "regexp";+ internal override string Type { get; } = "regexp";
}
public sealed record SetLocalValue(IEnumerable<LocalValue> Value) : LocalValue
{
- [JsonInclude]- internal string Type { get; } = "set";-}+ internal override string Type { get; } = "set";+}
Avoid repeating the discriminator property across all records; centralize it in a shared base/helper to prevent drift and reduce boilerplate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
🔗 Related Issues
Fixes #15621
💥 What does this PR do?
Adds missing type.
🔧 Implementation Notes
Moving polymorphic serialization discriminator for
LocalValueabstract type into derived types, which is explicitlynullforRemoteReferencetypes.🔄 Types of changes
PR Type
Bug fix, Enhancement
Description
Removed polymorphic serialization from
LocalValueabstract typeAdded
RemoteReferenceLocalValueabstract type with implementationsAdded explicit
Typeproperties to allLocalValuederived classesRefactored serialization to use individual type discriminators
Diagram Walkthrough
File Walkthrough
LocalValue.cs
Refactor LocalValue serialization and add RemoteReference typesdotnet/src/webdriver/BiDi/Script/LocalValue.cs
JsonPolymorphicattribute and allJsonDerivedTypedeclarationsfrom
LocalValuebase classRemoteReferenceLocalValueabstract record with two sealedimplementations:
SharedReferenceLocalValueandRemoteObjectReferenceLocalValueTypeproperty with[JsonInclude]attribute to all 16derived
LocalValueclasses for individual type discriminationRemoteReferenceLocalValuebranch alongside existing
PrimitiveProtocolLocalValueand other types