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

4

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?

azure
serialization
azureservicebus
azure-servicebus-queues
brokeredmessage
asked on Stack Overflow Apr 24, 2017 by Yasir • edited Apr 24, 2017 by Yasir

1 Answer

1

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>
answered on Stack Overflow Apr 25, 2017 by Tom Sun - MSFT • edited Apr 26, 2017 by Tom Sun - MSFT

User contributions licensed under CC BY-SA 3.0