2008-02-05

Xaml Serialization with WCF

So I'm reading RESTful Web Services and I got inspired. Sounds like a great way to expose data on the web.

I've heard great things about WCF support for REST, with their new WebGet attribute and such.

So I fire up Visual C# Express and get hacking.

Amazing what one can do in 54 lines of code.

using System;
using System.ServiceModel;
using System.ServiceModel.Web;
 
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
 
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
host.Open();
 
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
 
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
 
private int m_count;
}
 
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
 
public DateTime Now { get; set; }
public int Count { get; set; }
}
}
 

And we open a browser to http://localhost:8080/time/GetTime:

<DateTimeCount xmlns="http://schemas.datacontract.org/2004/07/J832" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<_x003C_Count_x003E_k__BackingField>0</_x003C_Count_x003E_k__BackingField>
<_x003C_Now_x003E_k__BackingField>2008-02-04T23:28:21.987561-08:00</_x003C_Now_x003E_k__BackingField>
</DateTimeCount>

Isn't that *cough* *choke* pretty *gag*.

I suddenly had a flash of nostalgia...of longing...for Xaml.

Xaml: that wonderful language where document-object mapping is implicit, trivial, and human-understandable.

(I'd love to link to Our 7 Goals of Xaml introduction but the link is dead. Help, Rob?)

Thankfully, XamlWriter is publicly available and WCF is insanely, gratuitously, wonderfully extensible.

  • WCF services have endpoints.
  • Endpoints have contracts.
  • Contracts have operations.
  • Operations have behaviors.
  • Behaviors have formatters.
  • Formatters can have custom messages--for instance, XamlMessage.

Thanks to Maheshwar Jayaraman for the post (and sample) on his old blog that got me started.

The code is a bit longer, but it's mostly boiler plate:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Windows.Markup;
using System.Xml;
 
[assembly: XmlnsDefinition("http://schemas.j832.com/demo/2008", "J832")]
 
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
 
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
foreach (OperationDescription operationDescription in endPoint.Contract.Operations)
{
operationDescription.Behaviors.Add(new MyOperationBehavior());
}
 
host.Open();
 
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
 
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
 
private int m_count;
}
 
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
 
public DateTime Now { get; set; }
public int Count { get; set; }
}
 
public class MyOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
 
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
 
public void ApplyDispatchBehavior(
OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new MyMessageFormatter();
}
 
public void Validate(OperationDescription operationDescription) { }
}
 
public class MyMessageFormatter : IDispatchMessageFormatter
{
public void DeserializeRequest(Message message, object[] parameters) { }
 
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return new XamlMessage(result);
}
}
 
public class XamlMessage : Message
{
public XamlMessage(object value){ Value = value; }
 
public override MessageHeaders Headers
{
get { return new MessageHeaders(MessageVersion.None); }
}
 
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
XamlWriter.Save(Value, writer);
}
 
public override MessageProperties Properties{ get{ return new MessageProperties(); } }
 
public override MessageVersion Version{ get{ return MessageVersion.None; }}
 
public object Value { get; private set; }
}
}

And (drumroll) the output from http://localhost:8080/time/GetTime:

<DateTimeCount Now="2008-02-04T23:53:18.884273-08:00" Count="14" xmlns="http://schemas.j832.com/demo/2008"/>

Isn't that pretty? The XmlnsDefinition assembly attribute is the icing that gives you the pretty xmlns.

Do other folks love Xaml? Do you wish it was available outside PresentationFramework.dll?

Let me know. I'm thinking of starting a campaign.

Hear that, Rob? :-)


kick it on DotNetKicks.com