From 2c7dcea3ec82684c4065f76d3110afe091e39366 Mon Sep 17 00:00:00 2001 From: Pramod Ubbala <16706816+upramod@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:09:14 -0700 Subject: [PATCH 1/3] docs: explain reading untyped values from AdditionalData --- docs/handling-unknown-additional-data.md | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/handling-unknown-additional-data.md diff --git a/docs/handling-unknown-additional-data.md b/docs/handling-unknown-additional-data.md new file mode 100644 index 00000000000..863c5d04f0d --- /dev/null +++ b/docs/handling-unknown-additional-data.md @@ -0,0 +1,40 @@ +# Reading unknown JSON properties from Microsoft Graph models + +The SDK stores response properties that are not represented by a generated model property in `AdditionalData`. In the Kiota based v5 SDK, JSON arrays and objects in this dictionary are represented by Kiota `UntypedNode` types, not `System.Text.Json.JsonElement`. Code that tests an additional-data value with `value is JsonElement` will therefore skip the value. + +For example, group delta responses can include the `members@delta` annotation. Read its member IDs from the untyped array: + +```csharp +using Microsoft.Graph.Models; +using Microsoft.Kiota.Abstractions.Serialization; + +var group = deltaResponse.Value?.FirstOrDefault(); +if (group is not null && + group.AdditionalData.TryGetValue("members@delta", out var value) && + value is UntypedArray members) +{ + foreach (var node in members.GetValue()) + { + if (node is not UntypedObject member) + { + continue; + } + + var properties = member.GetValue(); + if (!properties.TryGetValue("id", out var idNode) || + idNode is not UntypedString id) + { + continue; + } + + var memberId = id.GetValue(); + var isRemoved = properties.ContainsKey("@removed"); + + // Use memberId and isRemoved to apply the delta. + } +} +``` + +Treat `@removed` as a marker: its presence means the member was removed. Delta consumers should apply additions and removals idempotently and persist the returned delta link only after successfully applying the page. + +The concrete value types are `UntypedArray`, `UntypedObject`, and `UntypedString` from Kiota abstractions. Avoid serializing the SDK model with `System.Text.Json.JsonSerializer` to inspect these values; that serializer does not define the Kiota `UntypedNode` representation. From 872d9d5a9378947a5dc7ac0abc903dba719e9388 Mon Sep 17 00:00:00 2001 From: Pramod Ubbala <16706816+upramod@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:18:35 -0700 Subject: [PATCH 2/3] Update documentation on handling additional data Clarify handling of additional data in SDK documentation, including example of reading member IDs from untyped arrays. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/handling-unknown-additional-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handling-unknown-additional-data.md b/docs/handling-unknown-additional-data.md index 863c5d04f0d..a577d89e40f 100644 --- a/docs/handling-unknown-additional-data.md +++ b/docs/handling-unknown-additional-data.md @@ -2,7 +2,7 @@ The SDK stores response properties that are not represented by a generated model property in `AdditionalData`. In the Kiota based v5 SDK, JSON arrays and objects in this dictionary are represented by Kiota `UntypedNode` types, not `System.Text.Json.JsonElement`. Code that tests an additional-data value with `value is JsonElement` will therefore skip the value. -For example, group delta responses can include the `members@delta` annotation. Read its member IDs from the untyped array: +For example, group delta responses can include the `members@delta` annotation. The following snippet illustrates reading member IDs from the first group's untyped array; it is not a complete delta-sync loop. In a complete sync, process every group entry in `deltaResponse.Value` on every page, following all returned next links, and persist the final delta link only after successfully applying all changes: ```csharp using Microsoft.Graph.Models; From b931756b30d0426fc8ef77bc6dcd25f080efa8cf Mon Sep 17 00:00:00 2001 From: Pramod Ubbala <16706816+upramod@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:35:37 -0700 Subject: [PATCH 3/3] docs: inspect member deltas for every group in response page --- docs/handling-unknown-additional-data.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/handling-unknown-additional-data.md b/docs/handling-unknown-additional-data.md index a577d89e40f..7ed906945c0 100644 --- a/docs/handling-unknown-additional-data.md +++ b/docs/handling-unknown-additional-data.md @@ -2,17 +2,19 @@ The SDK stores response properties that are not represented by a generated model property in `AdditionalData`. In the Kiota based v5 SDK, JSON arrays and objects in this dictionary are represented by Kiota `UntypedNode` types, not `System.Text.Json.JsonElement`. Code that tests an additional-data value with `value is JsonElement` will therefore skip the value. -For example, group delta responses can include the `members@delta` annotation. The following snippet illustrates reading member IDs from the first group's untyped array; it is not a complete delta-sync loop. In a complete sync, process every group entry in `deltaResponse.Value` on every page, following all returned next links, and persist the final delta link only after successfully applying all changes: +For example, group delta responses can include the `members@delta` annotation. The following snippet processes the `members@delta` annotation for every group in one response page. It is not a complete delta-sync loop: follow every `@odata.nextLink`, process each page successfully, and persist the final `@odata.deltaLink` only after all changes have been applied: ```csharp using Microsoft.Graph.Models; using Microsoft.Kiota.Abstractions.Serialization; -var group = deltaResponse.Value?.FirstOrDefault(); -if (group is not null && - group.AdditionalData.TryGetValue("members@delta", out var value) && - value is UntypedArray members) +foreach (var group in deltaResponse.Value ?? Enumerable.Empty()) { + if (!group.AdditionalData.TryGetValue("members@delta", out var value) || + value is not UntypedArray members) + { + continue; + } foreach (var node in members.GetValue()) { if (node is not UntypedObject member)