Deserializing ServiceBus content in Azure Logic Ap

2019-05-01 03:15发布

问题:

I'm trying to read the content body of a message in an Azure Logic App, but I'm not having much success. I have seen a lot of suggestions which say that the body is base64 encoded, and suggest using the following to decode:

@{json(base64ToString(triggerBody()?['ContentData']))}

The base64ToString(...) part is decoding the content into a string correctly, but the string appears to contain a prefix with some extra serialization information at the start:

@string3http://schemas.microsoft.com/2003/10/Serialization/�3{"Foo":"Bar"}

There are also some extra characters in that string that are not being displayed in my browser. So the json(...) function doesn't accept the input, and gives an error instead.

InvalidTemplate. Unable to process template language expressions in action 'HTTP' inputs at line '1' and column '2451': 'The template language function 'json' parameter is not valid. The provided value @string3http://schemas.microsoft.com/2003/10/Serialization/�3{"Foo":"bar" } cannot be parsed: Unexpected character encountered while parsing value: @. Path '', line 0, position 0.. Please see https://aka.ms/logicexpressions#json for usage details.'.

For reference, the messages are added to the topic using the .NET service bus client (the client shouldn't matter, but this looks rather C#-ish):

await TopicClient.SendAsync(new BrokeredMessage(JsonConvert.SerializeObject(item)));

How can I read this correctly as a JSON object in my Logic App?

回答1:

You can use the substring function together with indexOf and lastIndexOf to get only the JSON substring.

Unfortunately, it's rather complex, but it should look something like this:

@json(substring(base64ToString(triggerBody()?['ContentData']), indexof(base64ToString(triggerBody()?['ContentData']), '{'), add(1, sub(lastindexof(base64ToString(triggerBody()?['ContentData']), '}'), indexof(base64ToString(triggerBody()?['ContentData']), '}')))))

More info on how to use these functions here.

HTH



回答2:

This is caused by how the message is placed on the ServiceBus, specifically in the C# code. I was using the following code to add a new message:

var json = JsonConvert.SerializeObject(item);
var message = new BrokeredMessage(json);
await TopicClient.SendAsync(message);

This code looks fine, and works between different C# services no problem. The problem is caused by the way the BrokeredMessage(Object) constructor serializes the payload given to it:

Initializes a new instance of the BrokeredMessage class from a given object by using DataContractSerializer with a binary XmlDictionaryWriter.

That means the content is serialized as binary XML, which explains the prefix and the unrecognizable characters. This is hidden by the C# implementation when deserializing, and it returns the object you were expecting, but it becomes apparent when using a different library (such as the one used by Azure Logic Apps).

There are two alternatives to handle this problem:

  • Make sure the receiver can handle messages in binary XML format
  • Make sure the sender actually uses the format we want, e.g. JSON.

Paco de la Cruz's answer handles the first case, using substring, indexOf and lastIndexOf:

@json(substring(base64ToString(triggerBody()?['ContentData']), indexof(base64ToString(triggerBody()?['ContentData']), '{'), add(1, sub(lastindexof(base64ToString(triggerBody()?['ContentData']), '}'), indexof(base64ToString(triggerBody()?['ContentData']), '}')))))

As for the second case, fixing the problem at the source simply involves using the BrokeredMessage(Stream) constructor instead. That way, we have direct control over the content:

var json = JsonConvert.SerializeObject(item);
var bytes = Encoding.UTF8.GetBytes(json);
var stream = new MemoryStream(bytes);
var message = new BrokeredMessage(stream, true);
await TopicClient.SendAsync(message);


回答3:

Paco de la Cruz solution worked for me, though I had to swap out the last '}' in the expression for a '{', otherwise it finds the wrong end of the data segment.

I also split it into two steps to make it a little more manageable.

First I get the decoded string out of the message into a variable (that I've called MC) using:

@{base64ToString(triggerBody()?['ContentData'])}

then in another logic app action do the substring extraction:

@{substring(variables('MC'),indexof(variables('MC'),'{'),add(1,sub(lastindexof(variables('MC'),'}'),indexof(variables('MC'),'{'))))}

Note that the last string literal '{' is reversed from Paco's solution.

This is working for my test cases, but I'm not sure how robust this is.

Also, I've left it as a String, I do the conversion to JSON later in my logic app.