Using JSON.NET as a default serializer in WCF HTTP/Web/REST (vNext)

Just yesterday a client walked up to me and asked how to replace the default JSON serializer in WCF’s web programming model (whether in .NET 3.5 or 4.0).
Honestly, this is not too easy – if you aim at adding a different JSON engine - like the wonderful JSON.NET – to the WCF pipeline you won’t have too much fun. A lot of plumbing works needs to be done. A more practical approach is to use Message or Stream as the response/request type and apply the serialization/deserialization inside the service façade operation implementation.

I thought there should be an easier and more HTTP/Web/Rest-style way to do this.

Good news: I found one.
Bad news: it is based on the WCF HTTP Preview 1 (and thus non-RTM bits)

To get you up and running with the ideas of WCF HTTP vNext please refer to the master–of-disaster-PM Glenn Block’s blog Smile

 

So, in order to support JSON.NET all I had to do is to write a custom media type processor by deriving from MediaTypeProcessor. Please also refer to Cibrax’ great blog entry about versioning REST services with processors.

Here is a little sample for the JsonNetProcessor class::

namespace Microsoft.ServiceModel.Http
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.ServiceModel.Description;
    using Microsoft.Http;
    using Newtonsoft.Json;
    public class JsonNetProcessor : MediaTypeProcessor
    {
        private Type parameterType;
        public JsonNetProcessor(HttpOperationDescription operation, MediaTypeProcessorMode mode)
            : base(operation, mode)
        {
            if (this.Parameter != null)
            {
                this.parameterType = this.Parameter.ParameterType;
            }
        }
        public override IEnumerable<string> SupportedMediaTypes
        {
            get
            {
                return new List<string> { "text/json", "application/json" };
            }
        }
        public override void WriteToStream(object instance, Stream stream, HttpRequestMessage request)
        {
            var serializer = new JsonSerializer();
            
            using (var sw = new StreamWriter(stream))
            using (var writer = new JsonTextWriter(sw))
            {
                serializer.Serialize(writer, instance);
            }
        }
        public override object ReadFromStream(Stream stream, HttpRequestMessage request)
        {
            var serializer = new JsonSerializer();
            using (var sr = new StreamReader(stream))
            using (var reader = new JsonTextReader(sr))
            {
                var result = serializer.Deserialize(reader, parameterType);
                return result;
            }
        }
    }
}

 

Very straight-forward and actually almost too easy. As JSON.NET also supports binary JSON, named BSON, I added another class BsonProcessor for this as well:

namespace Microsoft.ServiceModel.Http
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.ServiceModel.Description;
    using Microsoft.Http;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Bson;
    public class BsonProcessor : MediaTypeProcessor
    {
        private Type parameterType;
        public BsonProcessor(HttpOperationDescription operation, MediaTypeProcessorMode mode)
            : base(operation, mode)
        {
            if (this.Parameter != null)
            {
                this.parameterType = this.Parameter.ParameterType;
            }
        }
        public override IEnumerable<string> SupportedMediaTypes
        {
            get
            {
                return new List<string> { "application/bson" };
            }
        }
        public override void WriteToStream(object instance, Stream stream, HttpRequestMessage request)
        {
            var serializer = new JsonSerializer();
            
            using (var writer = new BsonWriter(stream))
            {
                serializer.Serialize(writer, instance);
            }
        }
        public override object ReadFromStream(Stream stream, HttpRequestMessage request)
        {
            var serializer = new JsonSerializer();
            using (var reader = new BsonReader(stream))
            {
                var result = serializer.Deserialize(reader, parameterType);
                return result;
            }
        }
    }
}
 
Well. This is it. No more buzz.
Now let’s register the new processor with the pipeline by implementing a custom host configuration:
public class MyResourceConfiguration : HostConfiguration
{
    public override void RegisterRequestProcessorsForOperation(HttpOperationDescription operation, IList<Processor> processors, MediaTypeProcessorMode mode)
    {
        processors.Add(new JsonNetProcessor(operation, mode));
        processors.Add(new BsonProcessor(operation, mode));
    }
    public override void RegisterResponseProcessorsForOperation(HttpOperationDescription operation, IList<Processor> processors, MediaTypeProcessorMode mode)
    {
        processors.Add(new JsonNetProcessor(operation, mode));
        processors.Add(new BsonProcessor(operation, mode));
    }
}
Done.
Looking forward to more Web/HTTP goodness for WCF vNext
(and yes, feel free to discuss with me why bother about WCF at all when it comes to
HTTP-close-to-the-metal-programming…).