Fix #237 - Support multi-dimensional arrays in DataItemJsonConverter (supersedes #238)#243
Merged
Merged
Conversation
Refactor WriteFieldValue to handle nested (multi-dimensional) arrays via a dedicated WriteArrayItems / WriteArrayItemValue pair instead of recursing back into WriteFieldValue with a sentinel empty-string field name. This: - Restores GeoJSON support: Polygon/MultiPolygon `coordinates` arrays (depth 3+) now serialize correctly instead of falling back to `System.Collections.Generic.List`1[System.Object]`. - Removes the implicit `IsNullOrWhiteSpace(propertyName)` sentinel so legitimate empty-string and whitespace property names are preserved. - Adds a MaxJsonDepth (64) guard that throws InvalidOperationException for pathological / recursively nested input instead of stack-overflowing the host process. Tests: - 17 new tests in DataItemJsonConverterTests covering GeoJSON Point/LineString/ Polygon, the full issue-237 document, mixed scalars/nulls/IDataItems inside nested arrays, dictionary-of-dictionary nested arrays, wide-array smoke, empty/whitespace property-name regression guards, deserialize/re-serialize round-trip, and depth-guard at and over the limit (arrays, objects, mixed). - All 125 Common.UnitTests pass; full solution test suite is green. Supersedes #238. Original fix authored by Mart Lankamp - thanks! Closes #237 Co-authored-by: Mart Lankamp <mart.lankamp@users.noreply.github.com>
|
Thank you! |
Adds a Notes section to the JSON extension README explaining that multi-dimensional (nested) arrays are now supported on write (GeoJSON LineString/Polygon/MultiPolygon) and that JSON serialization enforces a maximum combined object + array nesting depth of 64 to guard against stack overflow on pathological input. Adds a cross-reference from the SQL Server extension README since JsonFields nested writes go through the same serializer.
Collaborator
Author
|
@markjbrown, can you take a look at this. thucnguyen77 is working with GeoJSON and has a scenario that can leverage dmt with. |
Collaborator
|
Will approve. I understand why someone would want/need this. My only concern is performance impact if someone goes nuts and starts writing data that has extraordinary number of nested levels. Cosmos is optimized for indexing/search of nesting levels up to 3 layers deep. As the layers of nesting go up, the RU cost go up for both writes (due to indexing) and queries. (point reads are unaffected). Non-blocking. Just wanted to call out potential edge case. |
markjbrown
approved these changes
May 16, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Fixes #237 by adding multi-dimensional array support to
DataItemJsonConverter. Supersedes #238 — original fix authored by @mlankamp, refactored to address review feedback and harden against pathological input.What changed
WriteFieldValueno longer recurses into itself with a sentinel empty-string field name. Instead, two dedicated helpers handle in-array writes:WriteArrayItems(writer, items, includeNullFields, depth)— iterates items inside an already-opened JSON array.WriteArrayItemValue(writer, item, includeNullFields, depth)— single per-item dispatcher; the new nested-IEnumerablebranch opens an unnamed nested array viaWriteStartArray()/WriteEndArray().IsNullOrWhiteSpace(propertyName)sentinel from Fixes #237 - support multi-dimensional arrays #238 — legitimate empty-string and whitespace property names are now preserved verbatim.MaxJsonDepth = 64and anEnsureDepthguard threaded throughWriteDataItem,WriteFieldValue,WriteArrayItems, andWriteArrayItemValue. Pathological / recursively nested input now throwsInvalidOperationExceptionwith a clear message instead of stack-overflowing the host process.Why
The original PR #238 worked, but during review I had two design concerns:
""asfieldNameand branching onIsNullOrWhiteSpaceoverloads the method contract and makes legitimate empty-string keys ambiguous with the sentinel.Utf8JsonWriter's built-inMaxDepthis bypassed when callers useSkipValidation = true(the existing tests do), so we cannot rely on it. A deterministic exception is the right failure mode for a migration tool.Tests
17 new tests in
DataItemJsonConverterTests.cs:GeoJSON / issue #237 scenarios
GeoJsonPoint_Coordinates_OneDimensional(regression guard)GeoJsonLineString_Coordinates_TwoDimensionalGeoJsonPolygon_Coordinates_ThreeDimensionalAsJsonString_FullGeoJsonDocument— the exact failing document from Support multi-dimensional arrays when writing json (example geojson polygon) #237; asserts no moreSystem.Collections.Generic.Listleakage.Nested-array edge cases
[[]][[null,1],[null]][[1,"a",true],[2.5,null]]IDataItemDictionary<string,object?>Regression / contract preservation
"":...Deserialize→AsJsonStringround-trip on issue-237 docDepth guard / stack-safety
MaxJsonDepthsucceedsMaxJsonDepth + 1throwsIDataItemnesting atMaxJsonDepthsucceedsIDataItemnesting atMaxJsonDepth + 1throwsAll 125 Common.UnitTests pass (was 108; +17 new). Full solution test suite is green.
Compatibility
WriteFieldValueandWriteDataItempublic/internal signatures are unchanged — depth threading is via private overloads with default0.Extensions/*.arrayItem is not string && arrayItem is IEnumerablepredicate is intentionally kept broad to preserve backward compatibility with the outer field-write path. Tightening it (e.g., to excludebyte[]) is left as a follow-up.Closes #237
Closes #238