Power Automate is built on JSON (JavaScript Object Notation), every input and output will be in a JSON format, and that's great because its the best 😎. Its a universal language we can use to talk to any other system though their API's, but its not the only language. In fact the original Dynamics (which Dataverse and the Power Platform is built on) wasn't JSON, but XML (its why you can still do XML quires on Dataverse tables). XML is still widely used, so this blog is all about how you can handle it in your flow.
- What is XML
- NameSpaces
- Xpath
- Updating XML
1. What is XML
.
There is one call out I want to make here, and that the above doesn't work the same with namespaces.
If the XML had prefixes then they can be added and it works, but if its a just a namespace then the xpath now looks something like:
/library/*[local-name()='book']
If only one namespace in the entire XML, or if a specific namespace:
/library/*[local-name()='book' and namespace-uri()='http://example.com/library']
Power Automate has an xpath
The first expression splits the before and after location, creating our first array of 2 items.
split(triggerBody()['text'],'<location>')
The second splits the second item into 2, and then concats the first item from the first array with the second item from the second array.
concat(
outputs('Split_xml')[0]
,
split(outputs('Split_xml')[1],'<location/>')[1]
)
If we wanted to edit/add new tag, we would add our new tag between the above items.
JSON Manipulation
Power Automate uses JSON as its api language, its less verbose and the new standard. So in this approach we are going to convert to JSON, remove the tag, convert back to XML.
First we are going to use the xpath() function to get our location tag, as with JSON conversion we also need to convert it to xml first. We use the below expression to get the tag:
xpath(
xml(triggerBody()['text']))
,
'//*[local-name()="location"]'
)[0]
As it returns an array of all location tags we have to grab the first with the array position [0].
Call out, if you didn't select a specific item in the array, and went for the whole array, it would return a collection of base64 items (like files, showing $content-type and $content keys), so you would need to base64ToString() convert the $content.
One small problem, its also appends a attribute to the tag, xmlns="http://example.com/library", which normally wouldn't matter, but it will in this case (see later). Why does it do that, because of the namespace. As said before, if you didn't have the namespace then the below expression would work:
xpath(xml(triggerBody()['text'])),'//location')
If you wanted to target with a namespaces you would add it to the xpath, so ours would look like this:
/*[local-name()="location" and namespace-uri()="http://example.com/library"]/*[local-name()="location"]
The only reason I didn't include the and namespace is that it isn't necessary, as there is only one namespace in the XML.
So why is this a problem, well when you use local-name() it automatically adds the namespace as a. attribute to our xpath() output (to help track it in the future). Again David why is this a problem, well because of the next step, we plan to use the xpath() output to do a find and replace (our way if creating a kind of regex), and that wont work because now it doesn't match the tag in the XML.
The namespace shouldn't be added without the namespace in the xpath(), but Microsoft decided to add it anyway. Finally getting to the point now, it means that we need to remove the namespace parameter from our xpath() output, and you guessed it, the best way is to convert it to a string and replace it with ''.
replace(
string(
xpath(
xml(triggerBody()['text'])
,
'//*[local-name()="location"]'
)[0]
)
,
' xmlns="http://example.com/library"'
,
''
)
Callout, see that leading whitespace of xmkns, that's there on purpose, do not remove it
Now we have out tag that is an exact match, our second expression uses a replace() expression to replace it with ''.
replace(
triggerBody()['text'])
,
outputs('Get_Protection')
,
''
)
If you want to add a tag you could concat it to the xpath() output, and if you want to edit it you could just edit/rebuild the xpath() output.
All 3 ways to handle XML work, but in most cases I would say JSON convert is the best, with XML for any particularly awkward schemas, but I still love a good string split() 😎.
The flow can be found in a solution named XML here.
SOCIAL SHARE CARD GENERATOR