When the new Custom API component was introduced it made a lot of people happy, but the initial reaction was short-lived when I assumed that objects need to be linked to an entity in Dataverse – which kind of makes it a single-level JSON schema. But what if we want to have nested objects and how do we make that happen. I brought this as a discussion item on the Power Platform LEVEL UP community on Discord. And soon awesome community members and MSFT team members chimed in on this issue and I came to know that Custom API supports “expando” entity. So first let’s see what is this entity.

What is “expando” entity?

It is an Entity type object that does not have a name and members can be dynamically added and removed at will. This entity is not an actual table in Dataverse but a dynamic object.

The “expando” entity is not currently supported if you are invoking Custom API from SDK. Hopefully it will be supported in the next release.

Next, let’s see how to configure a Custom API that supports this type of entity.

How to configure Custom API requests & responses with “expando” entity?

When you create a request parameter and select Type as Entity during that process do not populate anything in Entity Logical Name. This is what creates an expando entity type.

Similarly, when you create response property and if Type is Entity; do not populate anything in Entity Logical Name.

This is shown in the following diagram:

The newer version of Custom API Manager in XrmToolBox now supports expando entity. More details can be found on this GitHub issue.

How would JSON schema look like?

Once you have configured your Custom API with expando entity in request and response; let us see how the JSON schema for both of them would look like.

The important thing to remember is when using expando type you will have to provide that type during a Web API call as shown below:

Sample Request JSON Payload

{
    "Payload": {
        "@odata.type": "#Microsoft.Dynamics.CRM.expando",
        "SuperHero": {
            "@odata.type": "#Microsoft.Dynamics.CRM.expando",
            "name": "Batman",
            "Powers": {
                "@odata.type": "#Microsoft.Dynamics.CRM.expando",
                "strength": 90,
                "durability": 100
            },
            "Publisher": {
                "@odata.type": "#Microsoft.Dynamics.CRM.expando",
                "name": "DC",
                "Address": {
                    "@odata.type": "#Microsoft.Dynamics.CRM.expando",
                    "city": "LA"
                }
            }
        }
    }
}

Sample Response JSON Payload

{
    "@odata.context": "https://demo.crm.dynamics.com/api/data/v9.2/$metadata#expando/$entity",
    "@odata.type": "#Microsoft.Dynamics.CRM.expando",
    "ResponseType": "success",
    "ResponseMessage": {
        "@odata.type": "#Microsoft.Dynamics.CRM.expando",
        "SuperHeroDetails": "Batman has 100 durability",
        "PublisherDetails": "Batman is featured in DC comics"
    }
}

As you can observe we have not defined any of the objects inside the “Payload” attribute of the Request JSON and neither did we define the Response structure. Do remember when passing the request you have to provide "@odata.type": "#Microsoft.Dynamics.CRM.expando" to the dynamic object as highlighted in the code above.

Now, let’s look at the code to understand how the parsing happens.

Parsing the JSON in Plugin Code

Once we have configured our Custom API, it is time to look at how the plugin is written:

if (context.MessageName.Equals("powm_DemoDynamic")) {
  try {
    Entity payload = (Entity) context.InputParameters["Payload"];

    Entity sh = (Entity) payload["SuperHero"];
    Entity shPowers = (Entity) sh["Powers"];
    Entity shPublisher = (Entity) sh["Publisher"];
    Entity shPublisherAddress = (Entity) shPublisher["Address"];

    Entity dynamicResponse = new Entity();
    dynamicResponse["ResponseType"] = "success";

    Entity respMessage = new Entity();
    respMessage["SuperHeroDetails"] = $ "{sh["name "]} has {shPowers["durability "]} durability";
    respMessage["PublisherDetails"] = $ "{sh["name "]} is featured in {shPublisher["name "]} comics";

    dynamicResponse["ResponseMessage"] = respMessage;

    context.OutputParameters["ResponsePayload"] = dynamicResponse;

  } catch (Exception ex) {
    tracer.Trace("SuperHero: {0}", ex.ToString());
    throw new InvalidPluginExecutionException("An error occurred in SuperHeroTest.", ex);
  }
} else {
  tracer.Trace("SuperHero plug-in is not associated with the expected message or is not registered for the main operation.");
}

As you can see on line 3 we retrieve the Payload request parameter as an Entity. But this entity object is not specific to any table in Dataverse but is a dynamic object. All the nested objects under this Payload request parameter is also of type Entity (as can be seen from line 5 to 8).

Similarly, when we want to create an expando type response; we have to create an Entity object without any logical name and any nested objects will follow the same process. This is highlighted in the code from lines 10 to 17.

Next, let us look at the execution from Postman.

Executing the Custom API

For more content subscribe to my blogs and follow me on:

Don’t forget to subscribe to my Power Platform ProDev Newsletter

Subscription received!

Please check your email to confirm your newsletter subscription.

Become one of my sponsors on GitHub

1 comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: