Search code examples
azureserializationazureservicebusazure-servicebus-queuesbrokeredmessage

Azure Service Bus BrokeredMessage GetBody method throws SerializationException when reading data sent through Rest API


I am sending a csv file to an Azure Service Bus Queue from a .NET 3.5 based C# solution. Since the Service Bus nuget packages aren't available in .NET 3.5, I am using the Rest API.

byte[] file = File.ReadAllBytes(@"VehicleContacts.csv");

string url = baseAddress + queueName + "/messages" + "?timeout=60&api-version=2013-08 ";

WebClient webClient = new WebClient();

webClient.Proxy = proxy;
webClient.Headers[HttpRequestHeader.Authorization] = token;

// Add Broker Properties
webClient.Headers.Add("BrokerProperties", "{ \"Label\":\"VehicleContactsSync\" }");

// Add Custom Properties 
webClient.Headers.Add("FileName", "VehicleContactsSyncFile");

webClient.UploadData(url, "POST", file);

The queue receives the file properly. On the receiving side, I can use .NET 4.5. So, I try to get the message body using the following code:

BrokeredMessage message = queueClient.Receive(TimeSpan.FromSeconds(5));
if (message != null)
{
    var contentType = message.ContentType;                        
    var body = message.GetBody<byte[]>();
}

Here I get the contentType as byte[] as expected. But when I try to get the Body, I get the following error:

System.Runtime.Serialization.SerializationException occurred
  HResult=0x8013150C
  Message=There was an error deserializing the object of type System.Byte[]. 
  The input source is not correctly formatted.
  Source=System.Runtime.Serialization
  StackTrace:
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
   at Microsoft.ServiceBus.Messaging.BrokeredMessage.GetBody[T](XmlObjectSerializer serializer)

Inner Exception 1:
XmlException: The input source is not correctly formatted.

What am I doing wrong and how can I fix it?


Solution

  • XmlException: The input source is not correctly formatted.

    According to your discription, we could know the body formatted is incorrect. In your case, it seem that you use the CSV format file.

    From the document, we could know that BrokeredMessage.GetBody<T> Method that deserializes the brokered message body into an object of the specified type by using the System.Runtime.Serialization.DataContractSerializer with a binary System.Xml.XmlDictionaryReader.

    What am I doing wrong and how can I fix it?

    According to the Azure service bus send message API, we know that we need to use the Xml format message body.

    So please have a try to change the file to the xml format then it will be solved.

    Edit:

    Please have a try to DataContractSerializer your data before you send it

     MemoryStream ms = new MemoryStream();
     DataContractSerializer serializer = new DataContractSerializer(typeof(byte[]));
     serializer.WriteObject(ms, file);
     byte[] body = ms.ToArray();
     WebClient webClient = new WebClient { Headers = { [HttpRequestHeader.Authorization] = token } };
     // Add Broker Properties
     webClient.Headers.Add("BrokerProperties", "{ \"Label\":\"VehicleContactsSync\" }");
     // Add Custom Properties 
     webClient.Headers.Add("FileName", "VehicleContactsSyncFile");
     webClient.UploadData(queueUrl, "POST", body);
    

    I did a demo for it, it works correctly on my side.

    1.Create a console project

    2.Create token for sending message

     public static string GetSasToken(string resourceUri, string keyName, string key, TimeSpan ttl)
            {
                var expiry = GetExpiry(ttl);
                string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
                HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
    
                var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
                var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
                    HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
                return sasToken;
            }
    
            private static string GetExpiry(TimeSpan ttl)
            {
                TimeSpan expirySinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1) + ttl;
                return Convert.ToString((int)expirySinceEpoch.TotalSeconds);
            }
    

    3.add creating queue function

     public static void CreateQueue()
            {
                var cs = "connection string";
                var queueName = "queuename";
                var namespaceManager = NamespaceManager.CreateFromConnectionString(cs);
                var messagingFactory = MessagingFactory.CreateFromConnectionString(cs);
                var ver = namespaceManager.GetVersionInfo();
    
                if (namespaceManager.QueueExists(queueName))
                {
                    namespaceManager.DeleteQueue(queueName);
                }
    
                namespaceManager.CreateQueue(queueName);
    }
    

    4.add receiving queue message function

    public static void ReceiveMessage()
            {
                BrokeredMessage message = queueClient.Receive();
                if (message != null)
                {
                    var body = message.GetBody<byte[]>(new DataContractSerializer(typeof(byte[])));
                    Console.WriteLine(body);
                }
                Console.ReadKey();
            }
    

    5. add sending message with http function

    public static void SendHttpMessage()
            {
                byte[] file = File.ReadAllBytes(@"C:\tom\test.txt");
                string queueUrl = "https://servicebusname.servicebus.windows.net/" + "SampleQueue" + "/messages?timeout=60&api-version=2013-08";
                string token = GetSasToken(queueUrl,
                    "Key name", "Key", TimeSpan.FromDays(1));
                MemoryStream ms = new MemoryStream();
                DataContractSerializer serializer = new DataContractSerializer(typeof(byte[]));
                serializer.WriteObject(ms, file);
                byte[] body = ms.ToArray();
                WebClient webClient = new WebClient { Headers = { [HttpRequestHeader.Authorization] = token } };
                // Add Broker Properties
                webClient.Headers.Add("BrokerProperties", "{ \"Label\":\"VehicleContactsSync\" }");
                // Add Custom Properties 
                webClient.Headers.Add("FileName", "VehicleContactsSyncFile");
    
                webClient.UploadData(queueUrl, "POST", body);
    
            }
    

    6.Test in the main function

      private static QueueClient queueClient;
    
    
            static void Main(string[] args)
            {
               CreateQueue();
               SendHttpMessage();
               ReceiveMessage();
            }
    

    7.During send message I use fiddler to catch the http request, detail please refer to the screenshot. We can see that the sent message has been formatted

    enter image description here

    1. Receive the message.

    enter image description here

    Package.config file

    <?xml version="1.0" encoding="utf-8"?>
    
        <packages>
          <package id="WindowsAzure.ServiceBus" version="4.0.0" targetFramework="net452" />
        </packages>