Deserializing ServiceBus content in Azure Logic App

自闭症网瘾萝莉.ら 提交于 2019-12-05 06:57:38

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);

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

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.

UPDATE

We have found that just occasionally (2 in several hundred runs) the text that we want to discard can contain the '{' character. I have modified our expression to explicitly locate the start of the data segment, which for me is:

'{"IntegrationRequest"'

so the substitution becomes:

@{substring(variables('MC'),indexof(variables('MC'),'{"IntegrationRequest"'),add(1,sub(lastindexof(variables('MC'),'}'),indexof(variables('MC'),'{"IntegrationRequest"'))))}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!