Get Lookup Display Name and Choice (Option Set) Value Label in a Single Query Using a Microsoft Dataverse Connector FormattedValue Property
One of the limitations of using Microsoft Dataverse connector in future compared to the classic workflow WAS the inability to get the display name of the lookup value or the label of the Choice (Option Set) value in one simple query. However, this is actually not the limitation of the platform itself. It seems that the Microsoft Dataverse actions did not use the odata.include-annotations preference with the value of OData.Community.Display.V1.FormattedValue which is required by the Web API to include formatted values in the response. If we query data using the Web API with OData.Community.Display.V1.FormattedValue, the system returns the record with properties that support formatted values. Even the Microsoft Dataverse (legacy) connector can return at least the label of the Choice (Option Set) value with "_*choicecolumn*_label" property.
Getting the formatted value is a functionality that we need quite often especially when composing
an email with the record details in the flow. Most of the time, we had to do workarounds like adding additional Get a record step for each lookup
or
using Expand Query to retrieve the lookup display name. Sometimes, we had to rely on
deprecated Microsoft Dataverse (legacy) connector
to
get "_choicecolumn_label" property
or use the string map entity to retrieve the label text against the entity
name and choice value or even Invoke an HTTP request. But today, that is not the case any more because Microsoft Dataverse
connecter can now return properties that support formatted values just like
Web API.
Here is the JSON output of the List records action in one of my flows.
The left side is one of the previous runs from history. Once I edited the flow
and saved it, the flow updates the Microsoft Dataverse steps itself and
the right side is the result when I re-submitted the same flow run. The
additional properties returned from the updated flow are
- OData.Community.Display.V1.FormattedValue for supported data types
- Whole Number, Duration, Timezone, Language
- Decimal Number, Floating Point Number, Currency
- Date and Time, Date Only
- Yes/No (Two Options), Choice (Option Set), Choices (Multi Select Option Set)
- Lookup, Polymorphic Lookups
- Microsoft.Dynamics.CRM.associatednavigationproperty for lookup columns
- Microsoft.Dynamics.CRM.lookuplogicalname (which was not returned in output JSON previously for certain exceptional cases like partyid of the Activity Party table)
- Microsoft.Dynamics.CRM.totalrecordcount and Microsoft.Dynamics.CRM.totalrecordcountlimitexceeded in the List records output body
- These two looks like the similar properties of EntityCollection object from the SDK and seems to be added for future enhancement. But for now, the value is always -1 for totalrecordcount and false for totalrecordcountlimitexceeded
💡 Tip
The new OData.Community.Display.V1.FormattedValue is only available in the Get a record and List records actions of Microsoft Dataverse connector and not in the output of the trigger. In the trigger output, the Choice value label is available as _choicecolumn_label parameter. But there is no lookup display name available in the output of the trigger and you will have to call another Get a record action to get the necessary values.
The new OData.Community.Display.V1.FormattedValue is only available in the Get a record and List records actions of Microsoft Dataverse connector and not in the output of the trigger. In the trigger output, the Choice value label is available as _choicecolumn_label parameter. But there is no lookup display name available in the output of the trigger and you will have to call another Get a record action to get the necessary values.
Unfortunately, those new properties are not yet available in the
Dynamic Content pane and you will have to enter the expression
manually in the following syntax.
columnname@OData.Community.Display.V1.FormattedValue
e.g. outputs('Get_an_Account_Record')?['body/statuscode@OData.Community.Display.V1.FormattedValue']
items('Apply_to_each')?['_ownerid_value@OData.Community.Display.V1.FormattedValue']
Use the Expression Builder to populate the expression above as the expression block (instead of entering the expression directly in the textbox).
If you are using other action than "Apply to each", here is how you can find out the expression for the
Dynamic Value to build the expression.
E.g. Let's say you are using the Select action from the Data Operation. Select the "Peek code" under ellipses (...) menu.
You will see the expression of the existing Dynamic Value in the code view.
Select and copy that expression without @ symbol and curly brackets { } (if there any) and append "@OData.Community.Display.V1.FormattedValue" after the logical name of the column. The final expression will look something like this.
item()?['_ownerid_value@OData.Community.Display.V1.FormattedValue']
Another way to get the expression is to
- place the cursor beside the dynamic value pill
- Ctrl + A to select all
- Ctrl + C to copy the whole textbox
- paste into Notepad of any kind of text editor
- remove the @ symbol in the beginning and curly braces { }
- add @OData.Community.Display.V1.FormattedValue after the column logical name
Enter the expression in the Expression Builder to get the formatted value of the column.
Here is the flow with Get a record action to retrieve some columns from
Account table which supports formatted values and uses the
Compose action to see the formatted output values.
When the flow above ran, it generated the formatted value output as in the
screenshot below.
Formatted Value for Choices Column 🔗
For Choices (Multi Select Option Set) data type, the formatted value returns the text value of the selected options separated by the semicolon and <space> even though the normal value is a comma-separated integer value.
E.g. Normal Value = "1,1000"
Formatted Value = "Business; Stakeholder"
Formatted Value Using Parse JSON 🔗
If you are not comfortable with writing an expression to get the @OData.Community.Display.V1.FormattedValue of the column and prefer to select from the list of Dynamic Value list, you can use the Parse JSON action. I will briefly show how it can be done but if you want to know more details about using Parse JSON action, you can check out this blog post by Priyesh Wagh.
🛈 Note
Using Parse JSON action instead of writing the formatted output parameter in an expression will be counted as an additional API request for each succeeded/failed step.
Using Parse JSON action instead of writing the formatted output parameter in an expression will be counted as an additional API request for each succeeded/failed step.
1. Run the Flow once and collect the Outputs from the "Get a record" or "List records" step.
2. Create a "Parse JSON" action, populate "Content" parameter with the body from the output of the previous step, click on "Generate from sample" and paste the output from the step 1.
3. You can now select the formatted values from the list of Dynamic Value. Unlike the output of the Microsoft Dataverse connector, it does not show the display name of the columns. Instead, you can see the same format of logicalname@OData.Community.Display.V1.FormattedValue.
🛈 Note
If your existing flow is not returning OData.Community.Display.V1.FormattedValue property, edit the flow and save it to update the Microsoft Dataverse steps.
If your existing flow is not returning OData.Community.Display.V1.FormattedValue property, edit the flow and save it to update the Microsoft Dataverse steps.
For those who prefer to follow the video tutorial, you can check out Neil Parkhurst's YouTube video.
If the requirement is to get the Choice labels from the Dataverse Trigger and not from the Get a Row or List Rows actions, the format is as follows.
triggerOutputs()?['body/column_label']
You can refer to this blog post from Priyesh for more details.
Summary
With the latest update of Microsoft Dataverse connector, we can now query the formatted values of supported data types such as lookup display name, choice (option set) value label, formatted date/time by using the OData.Community.Display.V1.FormattedValue property of the column in the expression.
Thanks a lot.
ReplyDeleteExplained this in a proper and crisp way. No extra documentation and also no short cuts.
Gave an example which could be used directly to create our query.
I'm glad you enjoyed the post and it's useful for you 😊
DeleteThis is very clear, thank you for sharing. I found that assigning a variable string (or variable array if multiple values) allowed me to use it as dynamic content. :)
ReplyDeleteThat's interesting approach. How do you assign a variable string though? Just OData.Community.Display.V1.FormattedValue in the expression?
DeleteI tried this approach and received an "invalid expression" message.
DeleteCan you please show me your expression that you're getting such error message?
DeleteIs 'fieldname@OData.Community.Display.V1.FormattedValue' supposed to automatically be listed under Dynamic content? I still only see the 'value' object which outputs the value of the option set, not the actual label. Using the CDS Current Environment connector 'Get Record' and have tried saving and reopening multiple times.
ReplyDeleteUnfortunately, those new properties are not yet available in the Dynamic Content pane and you will have to enter in the Expression builder manually with the following syntax.
Deleteoutputs('Get_a_Record')?['body/statuscode@OData.Community.Display.V1.FormattedValue']
Where in 'Get a record' or 'List records' is your expression used? I'm not sure where in the action step the expression is to be entered so the formatted value is displayed instead of the code.
DeleteThe expression needs to be used in the steps after 'Get a record' or 'List records' actions where you are going to reference the output of those action steps.
DeleteE.g. If the expression for Owner field of 'Get a record' action is something like this,
outputs('Get_a_Record')?['body/_ownerid_value']
write your expression as below to get the formatted value.
outputs('Get_a_Record')?['body/_ownerid_value@OData.Community.Display.V1.FormattedValue']
Check out this screenshot and it would be clearer for you
https://1.bp.blogspot.com/-s2iggLw5PjQ/XxbbN0bvHgI/AAAAAAABrnE/Y895U74sn8gBWjwGwPp7uR4LNVQ0q4irwCLcBGAsYHQ/d/Get%2BRecord%2BFlow.png
Let me know if you still cannot work it out and want me to upload the sample flow.
This comment has been removed by the author.
ReplyDeleteThank you for the reply. I see the call for the formatted values in the Compose Account Information action step but I am still unclear on where this code gets applied:
ReplyDeleteoutputs('Get_an_Account_Record')?['body/statuscode@OData.Community.Display.V1.FormattedValue']
Thank you again. I'm keenly interested in this because statuscode on the Order entity is the exact field I'm trying to retrieve formatted values for.
Here's an example for your scenario. E.g. You are retrieving the Order entity with List records action and send an email with the Order ID and Status Reason (statuscode) for each record.
Deletehttps://i.imgur.com/HAiELzo.png
You can add the formatted value of the statuscode field using an expression.
https://i.imgur.com/qNG9LHr.png
This is how it will be shown in your Send an email action
https://i.imgur.com/ucOcTk8.png
This is the output in the flow run history
https://i.imgur.com/W7I13nw.png
I figured it out. I was able to enter the code format you described in the expression field to set a variable:
ReplyDeleteitems('Apply_to_each_3')?['statuscode@OData.Community.Display.V1.FormattedValue']
I was getting an error because I was referencing the wrong Apply to each repeat step.
Thank you for clarifying!
I'm glad to know that it is finally working fine.
DeleteThank you for this post, it’s extremely helpful. Will this also work for Multi-Select Option Sets?
ReplyDeleteYes, it also works for Choices (Multi Select Option Set) columns. The formatted value returns the text value of the selected options separated by the semicolon and (E.g. "Business; Stakeholder")
DeleteI have updated the post to include that info. Thanks for your question.
http://linnzawwin.blogspot.com/2020/07/get-lookup-display-name-and-option-set.html#choices
You are the man Linn! This is the only article on the internet that has the right answer to this question. Thank you so much for blogging weekly.
ReplyDeleteThanks for the encouragement, Matthew. I will try my best to keep it up.
DeleteBtw, I updated this post and included the steps for using Parse JSON and select from the Dynamic Value list in citizen developer-friendly approach.
https://linnzawwin.blogspot.com/2020/07/get-lookup-display-name-and-option-set.html#parsejson
Have you managed to get annotations to work with large datasets?
ReplyDeleteI have an OData query that returns over 9000 records. Using the CDS "List Records (current environment)" will get me the records, but after the first 5000 records the annotations are dropped. I am guessing that it is not including the annotations setting in the header when it is requesting the next page of records, but I cannot see how to add this in myself.
Sorry, it took a while to reply this comment with my vacation and stuff.
DeleteI'm afraid you will have to disable the Pagination in the Settings and handle the paging by yourself with Skip Token. Check out my last blog post about it.
https://linnzawwin.blogspot.com/2021/01/retrieve-more-than-100000-dataverse.html
hi Linn, good read, thansk for sharing.
ReplyDeletequestion, how do you filter rows using the same formatted column?
i have been getting syntax error when trying to filter using formatted column for a choice a field.
any ideas?
Hi Khurram
DeleteFormatted columns are just virtual columns and you cannot filter based on the formatted values. You will need to use the backend value (e.g. GUID for lookup, integer for choice column, etc.)
If you need to filter based on the text value of the lookup column, you can inner join and filter it. Use FetchXML Builder tool for such filter expressions.
hi Linn,
DeleteI see your comments 'virtual columns' can not filter based on formatted values.
I have choice type field on table.
Is there any possibility to filter based on that fields label ?
For example ,
field name -> Gender (Choice)
first value -> Label : Man , Value : 100xxx
second Label : Women , Value : 1000xx1
is there any possibility filter by label for that table on power automate ? Thanks
@hazer, No, it's not possible to filter with the label of the options (Man, Woman, etc.).
DeleteThe only way is to filter by the integer value 100xxx, 100xx1, etc.
You may get the integer value of the option from the StringMap table using the label.
I can't figure out how to use this when I am creating a new record. I am converting a spreadsheet via Flow where I just have the label, but I need to set the underlying number for that label.
ReplyDeleteThe drop down on that entity field in Add a New Row shows the labels and code numbers but setting the field to the label throws an error, and I can't see (except String Maps) how to get the code.
This is only for the getting the data out of Dataverse and not for setting the data (create/update) into Dataverse. If the choice options are not changed a lot, I would normally use "Switch" condition or If() expression to set the hardcoded integer value based on the string value.
DeleteOtherwise, getting the string maps is the only way I'm afraid.
Hi Linn, This was very useful link.
ReplyDeleteBut I have an issue while using @OData.Community.Display.V1.FormattedValue as this says expression invalid.
My Parse JSON only returns "@Microsoft.Dynamics.CRM.associatednavigationproperty" and "@Microsoft.Dynamics.CRM.lookuplogicalname". @OData.Community.Display.V1.FormattedValue is not present. Can you help me with this.
What connector are you using for that particular step? Is it Microsoft Dataverse connector or Microsoft Dataverse (legacy) connector?
DeleteMicrosoft Dataverse connector
DeleteI've never experienced though. Please contact me in LinkedIn message or Twitter DM to share the screenshot of the output of your "List rows" step
DeleteHi Linn,
ReplyDeleteThis is amazing link to answer my question recently. But I still have a problem. as I can't load the picture in the comment, I created a post in the MSFT Forum. Here is the link:
https://community.dynamics.com/365/f/dynamics-365-general-forum/427836/get-the-option-set-value-label-in-a-single-query-using-a-cds-current-environment-and-populate-in-a-subject-through-power-automate
Can you check in and see. Thanks
Sorry for the late reply. I just checked out that forum thread and I am glad it was sorted.
DeleteHI Linn,
ReplyDeleteI have one query am using below flows which should get the lists of cases which not modified in last 48 hrs & send emial to owninguser for this fetchxml using older than 48 hr cases but here my problem is it should exclude weekend which is Friday & Saturday so that i shouldnot count how can i achieve this & found like DayofWeek we can use but not getting where to use before sending email ?
Any clue ?
I guess you will have to check DayofWeek for today and if it is Sunday or Monday, set the parameter to 96 hours instead of 48 hours (to cover the weekend Fri/Sat).
DeleteHere's the sample expression and you will have to replace your own time zone with 'New Zealand Standard Time' https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones
if
(
or
(
equals(dayOfWeek(convertFromUtc(utcNow(), 'New Zealand Standard Time')), 0),
equals(dayOfWeek(convertFromUtc(utcNow(), 'New Zealand Standard Time')), 1)
),
96,
48
)
Something new I learnt today and it helped me with my flow. Thank you for taking time to blog this.. Appreciate the effort.
ReplyDeleteGlad to know that it helped. Thanks for your encouraging comment.
DeletePlease help in this flow.
ReplyDelete1. Need to copy item from dataverse table to SharePoint list on creation of item.
Issue: once data is not in dataverse field it gives me an error saying "cannot be evaluated because property doesn't exist".
When data is in the dataverse field it runs successfully and copied in SharePoint list.
Hi Anurag
DeleteIs it similar to the issue in this link? I am not sure if it applies to your issue but can you try to re-create your flow and see if it solves the problem?
https://knowledge-junction.com/2020/03/16/power-platform-power-automate-issue-invalidtemplate-unable-to-process-template-language-expressions-in-action-create_item-the-template-language-expression-cannot-be-evaluated-becaus/
Thank you for this, it is really well explained and very helpful
ReplyDeleteI have a question regarding a specific requirement I have... My application supports English and French, and I need to produce an email that is bilingual. I am getting various field values, some of which include Lookups and Choice fields. For the lookups, I can use ExpandQuery to get the English and French fields from the related tables, but for Choice fields, there are no separate fields for each language, the "FormattedValue" is automatically retrieved based on the current user's preferred language.
Is there a way to retrieve a specific language (localization) for a Choice field?
English = 1033, French = 1036
Yes, you can
DeleteRetrieve from the String Map table as similar to the query in the following post but by adding additional filter
e.g. attributename eq 'xyz_choicecolumn' and objecttypecode eq 'xyz_tablename' and langid eq 1036 and attributevalue eq <>
https://himbap.com/blog/?p=3210
Hi! Thanks for a great post.I'm actually looking for an answer for a similar question. If dataverse environment has several language packs installed, how can I get a localized choice label for a specific language in Power Automate?
DeleteThank You for your detailed blogging with examples for this topic! It helped me with migration from D365 connector to DataVerse connector.
ReplyDeleteGlad to know that it helps with your connector migration. Good luck with that.
Delete