4

I created a scheduler that runs at a specific time and inserts a JSON message into a storage queue. The JSON message is fixed, I put it in the BODY field (which says text/plain) during scheduler setup. The message is:

{ "action": "SendReminderMessages" }

On the receiver side (a WebJob), I poll the queue and then try to deserialize the JSON in these messages. Instead of the expected JSON above, I get the JSON message wrapped in an XML message:

<?xml version="1.0" encoding="utf-16"?>
<StorageQueueMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ExecutionTag>(Some Hex String)</ExecutionTag>
  <ClientRequestId>(Some GUID)</ClientRequestId>
  <ExpectedExecutionTime>2015-09-17T07:00:00</ExpectedExecutionTime>
  <SchedulerJobId>reminder-mails</SchedulerJobId>
  <SchedulerJobCollectionId>scheduler-jobs</SchedulerJobCollectionId>
  <Region>West Europe</Region>
  <Message>{ "action": "SendReminderMessages" }</Message>
</StorageQueueMessage>

How do I send the JSON message as-is, i.e. without the envelope?

theDmi
  • 17,546
  • 6
  • 71
  • 138

3 Answers3

1

Scheduler currently adds an XML wrapper to the body as a means to pass the job metadata. You can use the StorageQueueMessage class (under Microsoft.WindowsAzure.Scheduler.Models) from the Scheduler SDK that would properly deserialize the message. You can vote for the Azure Scheduler feature to add the ability to not include the wrapper on our Azure Scheduler User Voice forum.

Kevin Lam
  • 129
  • 3
  • I can't seem to access the link you posted. Seems to be a `/admin` route. – Aaron Hoffman Mar 01 '16 at 03:15
  • @KevinLam the StorageQueueMessage does not 'properly deserialize the message', it's a model class only. *Edit* Found you can deserialize using System.Xml.Serialization.XmlSerializer. ... Though the StorageQueueMessage has DataContract attributes, the DataContractSerializer does not accept this XML because it was not serialized with the namespace declarations. – Iain May 24 '16 at 02:34
0

That sounds very strange, I do the exact same thing only with e-mails and it works just fine for me. I've added my code, I hope it will help you spot your bug.

/// <summary>
/// Create or update a queue
/// </summary>
/// <param name="queueName">Name of queue</param>
/// <param name="message">Message to post in the queue</param>
/// <returns>Return true on success, false if already exists, throw exception on error</returns>
public bool PutMessage(string queueName, CloudQueueMessage message)
{
    try
    {
        var queue = queueContext.QueueClient.GetQueueReference(queueName);
        queue.AddMessage(message);
        return true;
    }
    catch (StorageClientException ex)
    {
        if ((int)ex.StatusCode == 404) return false;
        throw;
    }
}

/// <summary>
/// Serializes a e-mail to Json
/// </summary>
/// <param name="from">Sender e-mail address</param>
/// <param name="to">Recipient e-mail address</param>
/// <param name="subject">E-mail subject text</param>
/// <param name="body">E-mail body text</param>
/// <returns>Json</returns>
string SerializeEmail(string from, string to, string subject, string body)
{
    var email = new EmailEntity()
                    {
                        From = from,
                        To = to,
                        Subject = subject,
                        Body = body
                    };

    var stream = new MemoryStream();
    var serializer = new DataContractJsonSerializer(typeof(EmailEntity));
    serializer.WriteObject(stream, email);
    var serializedEmail = Encoding.Default.GetString(stream.ToArray());
    stream.Close();
    return serializedEmail;
}

/// <summary>
/// Puts a e-mail on the Azure queue to be sent
/// </summary>
/// <param name="from">Sender e-mail address</param>
/// <param name="to">Recipient e-mail address</param>
/// <param name="subject">E-mail subject text</param>
/// <param name="body">E-mail body text</param>
/// <returns>Return true on success, false if already exists, throw exception on error</returns>
public bool SendEmail(string from, string to, string subject, string body)
{
    var serializedEmail = SerializeEmail(from, to, subject, body);
    var message = new CloudQueueMessage(serializedEmail);
    return PutMessage(AzureQueueContext.EmailQueueName, message);
}

/// <summary>
/// Retrieve the next message from a queue
/// </summary>
/// <param name="queueName">Name of queue</param>
/// <param name="message">Message</param>
/// <returns>Return true on success (message available), false if no message or no queue, throw exception on error</returns>
bool GetMessage(string queueName, out CloudQueueMessage message)
{
    message = null;

    try
    {
        var queue = GetCloudQueueClient().GetQueueReference(queueName);
        message = queue.GetMessage();
        return message != null;
    }
    catch (StorageClientException ex)
    {
        if ((int)ex.StatusCode == 404) return false;

        throw;
    }
}

/// <summary>
/// Deserializes a e-mail message
/// </summary>
/// <param name="message">Message serialized in the Json inerchange format</param>
/// <returns>Deserialized e-mail as a EmailEntity object</returns>
EmailEntity DeserializeEmail(CloudQueueMessage message)
{
    var stream = new MemoryStream(Encoding.Default.GetBytes(message.AsString));
    var ser = new DataContractJsonSerializer(typeof(EmailEntity));
    var email = (EmailEntity)ser.ReadObject(stream);
    stream.Close();
    return email;
}

/// <summary>
/// Retrieve the next message from a queue
/// </summary>
/// <param name="email">E-mail</param>
/// <returns>Return true on success (message available), false if no message or no queue, throw exception on error</returns>
public bool GetEmail(out EmailEntity email, out CloudQueueMessage message)
{
    var result = GetMessage(AzureQueueContext.EmailQueueName, out message);
    email = result ? DeserializeEmail(message) : null;
    return result;
}
  • 2
    Your code indicates that you insert messages yourself. In my case, the Azure Scheduler inserts it. So obviously the envelope is added by the scheduler. – theDmi Sep 18 '15 at 06:08
  • @theDmi Sounds like it, yes. Is it an option for you to insert the messages yourself like I do? –  Sep 18 '15 at 07:00
0

I previously resolved this by creating a completely separate WebJob that reads the XML, pulls the JSON out, and re-queues the JSON value into the Storage Queue (solution here: https://github.com/Stonefinch/AzureIntro/tree/master/src/AzureIntro.WebJobs.AzureScheduler)

However, I recently found that using Azure Functions to queue a message provides a decent workaround. More info here: http://aaron-hoffman.blogspot.com/2017/01/replace-azure-scheduler-with-azure.html

Aaron Hoffman
  • 6,604
  • 8
  • 56
  • 61