Skip to content

Fix JSON serialization of nested Entity/EntityCollection in expando API responses#752

Merged
MarkMpn merged 1 commit into
masterfrom
fix/743-expando-entity-json-serialization
Apr 4, 2026
Merged

Fix JSON serialization of nested Entity/EntityCollection in expando API responses#752
MarkMpn merged 1 commit into
masterfrom
fix/743-expando-entity-json-serialization

Conversation

@MarkMpn

@MarkMpn MarkMpn commented Mar 30, 2026

Copy link
Copy Markdown
Owner

Problem

Fixes #743

When a custom API returns an expando EntityCollection whose entities contain nested Entity or EntityCollection attribute values, the serialization produced verbose .NET-style JSON instead of the flat WebAPI-style JSON:

Before (broken):

{
  "user": {
    "Attributes": [{"Key": "systemuser", "Value": "a81ca1d0-..."}],
    "LogicalName": null,
    "Id": "00000000-..."
  }
}

After (fixed):

{
  "user": {
    "@odata.type": "systemuser",
    "@odata.id": "a81ca1d0-...",
    "__DisplayName__": "John Doe"
  }
}

Root Cause

In ExecuteMessageNode.SerializeAttributeValues(), the else branch passed Entity and EntityCollection attribute values directly to JsonConvert.SerializeObject() which used default .NET reflection serialization, producing the Attributes[] / Entities[] verbose format.

Fix

Refactored SerializeAttributeValues into a BuildAttributeDictionary() helper that returns a Dictionary<string, object>, adding recursive handling for:

  • Entity attribute values → nested JSON object via recursion into BuildAttributeDictionary
  • EntityCollection attribute values → JSON array of recursively serialized entity dictionaries

All existing type handling (OptionSetValue, Money, EntityReference, @odata.type annotations) is preserved unchanged.

Changes

  • MarkMpn.Sql4Cds.Engine/ExecutionPlan/ExecuteMessageNode.cs — Core fix: refactored SerializeAttributeValues with a recursive BuildAttributeDictionary helper
  • MarkMpn.Sql4Cds.Engine.Tests/ExpandoMessageExecutor.cs — New fake executor returning entities with nested Entity attributes
  • MarkMpn.Sql4Cds.Engine.Tests/StubMessageCache.cs — Added ExpandoMessage entry for testing
  • MarkMpn.Sql4Cds.Engine.Tests/FakeXrmEasyTestsBase.cs — Registered ExpandoMessageExecutor
  • MarkMpn.Sql4Cds.Engine.Tests/JsonFunctionTests.cs — New test verifying flat WebAPI-style JSON output
  • MarkMpn.Sql4Cds.Engine.Tests/MarkMpn.Sql4Cds.Engine.Tests.csproj — Added new executor file to project

…PI responses

When a custom API returns an expando EntityCollection whose entities
contain nested Entity or EntityCollection attribute values, the
serialization previously fell into the default else branch and called
JsonConvert.SerializeObject directly on the CRM SDK types. This produced
verbose .NET-style JSON (Attributes[], Entities[], etc.) instead of the
flat WebAPI-style JSON expected for use with JSON_VALUE/JSON_QUERY.

Refactored SerializeAttributeValues into a BuildAttributeDictionary
helper method that recursively handles:
- Entity attribute values → nested JSON object via recursion
- EntityCollection attribute values → JSON array of nested objects

Added ExpandoMessage test fixture and a new test verifying that
nested Entity attributes in expando responses are serialized as flat
WebAPI-style JSON objects rather than verbose .NET format.

Fixes #743

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

@MarkMpn MarkMpn merged commit 08165cf into master Apr 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]

1 participant