If you're using .NET, you already knew how easy it is to return objects and have them serialized in JSON in Web Services. All we need to do is to deal with the logics and .NET does all the things and tricks related to the communication. For our example, this is the method that needed to be called periodically from the client:
[WebMethod]
public IEnumerable<ChatMessage> GetMessages()
{
var currentPlayer = OnlinePlayers.Single(p => p.Id == CurrentPlayerId);
var messages = Messages.Where(m => m.DateSent > currentPlayer.DateSynced);
currentPlayer.DateSynced = DateTime.Now;
return messages;
}
The problem is that .NET really serializes everything. If we have the following definitions:
[Serializable]
public class Player
{
public Player() { }
public string Name { get; set; }
public string Id { get; set; }
public string PhotoUrl { get; set; }
public DateTime DateSynced { get; set; }
// other properties
}
[Serializable]
public class ChatMessage
{
public ChatMessage() { }
public Player Sender { get; set; }
public string Message { get; set; }
public DateTime DateSent { get; set; }
}
It is certainly not desirable to send a Player object back to the client by every ChatMessage. Assuming that the client already knows all the Players involved in the Chat, the ChatMessage object that is being sent to the client only needs to have a Sender ID property. One obvious solution that is very familiar for hardcore client-server developers is to have another type, let's say ClientChatMessage that only contains the required information. If you're taking this approach remember ClientChatMessage should be a struct type:
[WebMethod]
public IEnumerable<ClientChatMessage> GetMessages()
{
//...
return messages.Select(m => (ClientChatMessage)m);
}
[Serializable]
public struct ClientChatMessage
{
public string SenderId;
public DateTime DateSent;
public string Message;
public static explicit operator ClientChatMessage(ChatMessage m)
{
return new ClientChatMessage()
{
DateSent = m.DateSent,
Message = m.Message,
SenderId = m.Sender.Id
};
}
}
When we are targeting only AJAX clients it's very handy to return anonymously typed objects. Anonymous objects cannot be serialized by XML serialization, but JavaScript (JSON) serializer is able to serialize them. I prefer it:
[WebMethod]
public IEnumerable<Object> GetMessages()
{
//...
return messages.Select(m =>
new {
s = m.Sender.Id,
m = m.Message,
d = m.DateSent
});
}
Here I've shortened the names of the properties, hey it is AJAX, we should save bandwidth by every mean possible.
We've used this trick in many places of the games we are developing for Hyzonia, as the current games only will be available for JavaScript clients. We found another similar trick very handy: a web method can take an object as an argument; if you send a JSON serialized object from the client to these methods, you'll have a Dictionary<String, Object> in the server side.