Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Azure WebJob ServiceBus deserializing XML by default?

I have a simple Azure WebJobs ServiceBusTrigger that looks like

public static async void ProcessQueueMessage([ServiceBusTrigger("myqueuename")] String json, TextWriter log) { ... }

Unfortunately, it is failing to deserialize JSON as XML (not surprising). I have inspected the payload and confirmed that it is just a UTF-8 encoded array of bytes. I have two questions.

  1. Why is it assuming that my String is XML?
  2. How do I tell it no, there is no XML, there is just a string?

Stack trace:

System.InvalidOperationException: Exception binding parameter 'json' ---> System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type System.String. The input source is not correctly formatted. ---> System.Xml.XmlException: The input source is not correctly formatted.
 at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
 at System.Xml.XmlBufferReader.ReadValue(XmlBinaryNodeType nodeType, ValueHandle value)
 at System.Xml.XmlBinaryReader.ReadNode()
 at System.Xml.XmlBinaryReader.Read()
 at System.Xml.XmlBaseReader.IsStartElement()
 at System.Xml.XmlBaseReader.IsStartElement(XmlDictionaryString localName, XmlDictionaryString namespaceUri)
 at System.Runtime.Serialization.XmlReaderDelegator.IsStartElement(XmlDictionaryString localname, XmlDictionaryString ns)
 at System.Runtime.Serialization.XmlObjectSerializer.IsRootElement(XmlReaderDelegator reader, DataContract contract, XmlDictionaryString name, XmlDictionaryString ns)
 at System.Runtime.Serialization.DataContractSerializer.InternalIsStartObject(XmlReaderDelegator reader)
 at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
 at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
 --- End of inner exception stack trace ---
 at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
 at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
 at Microsoft.ServiceBus.Messaging.DataContractBinarySerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
 at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlReader reader, Boolean verifyObjectName)
 at System.Runtime.Serialization.XmlObjectSerializer.InternalReadObject(XmlReaderDelegator reader, Boolean verifyObjectName)
 at System.Runtime.Serialization.XmlObjectSerializer.InternalReadObject(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
 at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
 at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
 at Microsoft.ServiceBus.Messaging.DataContractBinarySerializer.ReadObject(Stream stream)
 at Microsoft.ServiceBus.Messaging.BrokeredMessage.GetBody[T](XmlObjectSerializer serializer)
 at Microsoft.ServiceBus.Messaging.BrokeredMessage.GetBody[T]()
 at Microsoft.Azure.WebJobs.ServiceBus.Triggers.BrokeredMessageToStringConverter.ConvertAsync(BrokeredMessage input, CancellationToken cancellationToken)
 at Microsoft.Azure.WebJobs.ServiceBus.Triggers.ConverterArgumentBindingProvider`1.ConverterArgumentBinding.<BindAsync>d__0.MoveNext()

Edit: The WebJobs documentation suggests that not only should what I did work (String) but ServiceBusTrigger should automatically deserialize JSON objects. However, if I try to get my POCO out I still get an XML deserialization error. Interestingly, I also get an XML deserialization error if I set the type as Byte[], which is also supposed to work.

Edit 2: Stream also does not work. It appears that only BrokeredMessage works for the trigger, and GetBody is the only way I can find to get the String out of the BrokeredMessage.

like image 943
Micah Zoltu Avatar asked Jan 05 '15 05:01

Micah Zoltu


People also ask

Is Microsoft Azure ServiceBus deprecated?

This package has been deprecated. Please note, a newer package is available at https://nuget.org/packages/Azure.Messaging.ServiceBus as of 11/2020. While this package will continue to receive critical bug fixes, we strongly encourage you to upgrade.

What is Microsoft ServiceBus messaging?

A client that can create Sender instances for sending messages to queues and topics as well as Receiver instances to receive messages from queues and subscriptions.

What is ServiceBus Windows net?

Azure Service Bus is an asynchronous messaging cloud platform that enables you to send data between decoupled systems. Microsoft offers this feature as a service, which means that you don't need to host your own hardware to use it.


2 Answers

I was able to get deserialization work by adding a custom message processor.

Custom message processor example is available at https://github.com/Azure/azure-webjobs-sdk-samples/tree/master/BasicSamples/MiscOperations

You set the ContentType to application/json as below -

public class CustomMessagingProvider : MessagingProvider
{
    private readonly ServiceBusConfiguration _config;

    public CustomMessagingProvider(ServiceBusConfiguration config) : base(config)
    {
        _config = config;
    }

    public override MessageProcessor CreateMessageProcessor(string entityPath)
    {
        return new CustomMessageProcessor(_config.MessageOptions);
    }

    private class CustomMessageProcessor : MessageProcessor
    {
        public CustomMessageProcessor(OnMessageOptions messageOptions)
            : base(messageOptions)
        {
        }

        public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken)
        {
            message.ContentType = "application/json";

            return base.BeginProcessingMessageAsync(message, cancellationToken);
        }
    }
}

You then set this message processor when configuring ServiceBusConfiguration in webjob.

 JobHostConfiguration config = new JobHostConfiguration();
        ServiceBusConfiguration serviceBusConfig = new ServiceBusConfiguration
        {
            ConnectionString = _servicesBusConnectionString
        };

        serviceBusConfig.MessagingProvider = new CustomMessagingProvider(serviceBusConfig);

        config.UseServiceBus(serviceBusConfig);
like image 82
Mahesh Kshirsagar Avatar answered Oct 19 '22 22:10

Mahesh Kshirsagar


If your payload is String the you have two options:

  1. Change the parameter type to BrokeredMessage and then deserialize it yourself
  2. Set the BrokeredMessage.ContentType property to text/plain (this assumes that you have control over the code that generates the message)

Except for the weird String case in which you need the content type, the rule is that service bus payloads can be deserialzed only to the same object as the payload. That's because of the ServiceBus binary serializer.

like image 25
Victor Hurdugaci Avatar answered Oct 19 '22 22:10

Victor Hurdugaci