Skip to content

CSHARP-5942: Fix serializer resolution for dictionary paths with numeric string keys#1923

Open
damieng wants to merge 6 commits intomongodb:mainfrom
damieng:csharp5942
Open

CSHARP-5942: Fix serializer resolution for dictionary paths with numeric string keys#1923
damieng wants to merge 6 commits intomongodb:mainfrom
damieng:csharp5942

Conversation

@damieng
Copy link
Copy Markdown
Contributor

@damieng damieng commented Mar 24, 2026

Summary

  • Fix DictionarySerializerBase<TDictionary, TKey, TValue>.TryGetItemSerializationInfo() to return false for Document-represented dictionaries with non-numeric key types
  • Add tests covering AddToSet with dictionary-based nested collection paths, including numeric string keys

Background

A regression was introduced in 3.7.0 via CSHARP-5572 (#1700) that changed DictionarySerializerBase<TDictionary, TKey, TValue>.TryGetItemSerializationInfo() to always return true, even for DictionaryRepresentation.Document. Previously it returned false for Document representation.

This broke string-based field path resolution for dictionaries with all-digit keys (e.g., "Buckets.10"), because StringFieldDefinitionHelper.Resolve treats all-digit path segments as array indices and calls IBsonArraySerializer.TryGetItemSerializationInfo(). When the dictionary serializer now returns true, the resolver picks up a KeyValuePairSerializer instead of the dictionary's value serializer. Operations like AddToSet, Push, and Pull then fail because KeyValuePairSerializer does not implement IBsonArraySerializer.

Root cause

In CSHARP-5572, DictionarySerializerBase.TryGetItemSerializationInfo() was simplified to unconditionally return true. The original code returned false for DictionaryRepresentation.Document, which caused StringFieldDefinitionHelper.Resolve to correctly fall through the array-index branch and set resolvedFieldSerializer = null, letting AddToSet resolve the item serializer from the registry.

Fix

Restore the guard in TryGetItemSerializationInfo() for Document-represented dictionaries, but with a refinement: allow true when TKey is a numeric type (int, long, short, byte, etc.), since dictionaries with integer keys serialized as Document look like BSON arrays with numeric indices. For string-keyed dictionaries — the common case — the method returns false for Document representation, matching the pre-3.7.0 behavior.

if (_dictionaryRepresentation == DictionaryRepresentation.Document && !IsNumericType(typeof(TKey)))
{
    serializationInfo = null;
    return false;
}

@damieng damieng added the bug Fixes issues or unintended behavior. label Mar 24, 2026
@damieng damieng marked this pull request as ready for review March 24, 2026 13:13
@damieng damieng requested a review from a team as a code owner March 24, 2026 13:13
@damieng damieng requested review from Copilot and kyra-rk March 24, 2026 13:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression in string-based field path serializer resolution where all-digit path segments (e.g., "Buckets.10") were incorrectly treated as array indexes when navigating through dictionaries with DictionaryRepresentation.Document, leading to update operators like $addToSet failing due to the wrong serializer being selected.

Changes:

  • Updates StringFieldDefinitionHelper.Resolve to handle "$" and all-digit segments separately, attempting document-member resolution for numeric segments before falling back to array-item resolution.
  • Adds regression tests covering AddToSet/AddToSetEach with string and expression paths through dictionaries (including numeric string keys and different dictionary/value shapes).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/MongoDB.Driver/FieldDefinition.cs Adjusts serializer resolution logic for numeric path segments to prefer document-member resolution (fixing numeric dictionary key paths).
tests/MongoDB.Driver.Tests/UpdateDefinitionBuilderTests.cs Adds regression tests to ensure update rendering works for dictionary paths (including numeric string keys).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kyra-rk kyra-rk requested review from sanych-sun and removed request for kyra-rk March 24, 2026 13:19
@sanych-sun
Copy link
Copy Markdown
Member

I think we have to double-check the changes done to DictionarySerializerBase<TDictionary, TKey, TValue>.TryGetItemSerializationInfo(). This method is a part of IBsonArraySerializer, when arrays are defined as:

A BSON document with integer values for the keys, starting at 0 and continuing sequentially. For example, the array ['red', 'blue'] encodes as the document {'0': 'red', '1': 'blue'}.
https://bsonspec.org/spec.html#more-array

It seems like we might want to double-check if the Key has int-based representation, and return false if it is not.

Copy link
Copy Markdown
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but please double-check the comment in tests, it seems to be misleading.

[Fact]
public void AddToSet_with_string_path_through_array_rep_dictionary()
{
// When dictionary uses ArrayOfDocuments representation, string path resolution
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment is wrong, the issue was about the case when dictionary represented as Document. Checks in DictionarySerializerBase.TryGetItemSerializationInfo suggests there are no problem with ArrayOfDocuments representation too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Fixes issues or unintended behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants