-
Notifications
You must be signed in to change notification settings - Fork 1
Implementing a Codec
A codec’s raison d’etre is to take Resources (POCOs) and either write (enCOde) them to a stream or read (DECode) them from a stream. In OpenRasta, this means implementing one or both of IMediaTypeWriter and IMediaTypeReader.
Most of the time, writing a codec isn’t too complex. You only have to look at the source for OpenRasta’s own JsonDataContractCodec or XmlSerializerCodec to see this. In both of these examples, the codec is taking an existing .NET serialization mechanism and simply writing the tiny amount of OpenRasta plumbing necessary to make it work.
Let’s take a look at the JsonDataContractCodec source code to see how it implements reading and writing resources to the JSON format using existing .NET classes. The whole class isn’t too big, so we might as well take a look at the whole thing, then break it down bit by bit:
[["json")|MediaType("application/json;q=0.5",]]
public class JsonDataContractCodec : IMediaTypeReader, IMediaTypeWriter
{
public object Configuration { get; set; }
public object ReadFrom(IHttpEntity request, IType destinationType, string paramName)
{
if (destinationType is IMember)
return new DataContractJsonSerializer(destinationType.StaticType).ReadObject(request.Stream);
return Missing.Value;
}
public void WriteTo(object entity, IHttpEntity response, string[] parameters)
{
if (entity == null)
return;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(entity.GetType());
serializer.WriteObject(response.Stream, entity);
}
}
Let’s start at the top with the MediaType
attribute. This is the place where you specify some good defaults for the media type – its mime type, its quality rating and what extension it will use if you’re using the ContentTypeExtensionUriDecorator. Specifying these defaults here lets you avoid cluttering your fluent configuration later with repeated directives such as .ForMediaType("application/json")
.
Both IMediaTypeWriter
and IMediaTypeReader
inherit the responsibility to implement ICodec
, which has a single Configuration {get; set;}
member. We have no special configuration requirements for the codec (in fact, most codecs don’t) so it’s sufficient for us to supply this property. We won’t use it here.
public object ReadFrom(IHttpEntity request, IType destinationType, string paramName)
{
if (destinationType is INativeMember)
return new DataContractJsonSerializer(((INativeMember)destinationType).NativeType).ReadObject(request.Stream);
return Missing.Value;
}
This method is going to take some input from request a stream and hydrate it to a type we know about. First, though, we need to check that we ’’’can’’’ hydrate the type at all.
Because OpenRasta supports a dynamic type system, in which the type of an object may not be an actual CLR Type (but could in fact be a DLR object, a script or nearly any other type system), it provides an IType
interface with which you can inspect what object members (properties, methods, etc) exist on said IType
.
As the DataContractJsonSerializer
can only create objects backed by a CLR Type, we first try to see if the IType
interface can return us a CLR Type. If we can cast the destinationType
parameter to INativeMember
, then it’s ok to hydrate the object as it’s a CLR Type
the DataContractJsonSerializer
can use. If not, we will return Missing.Value
to indicate we couldn’t deserialize anything from the request stream. Once we know we can serialize the type, we do so with the .NET DataContractJsonSerializer.ReadObject
method – using the IHttpEntity.Stream
that’s available to us through the request
parameter. That’s it – we’re done!
public void WriteTo(object entity, IHttpEntity response, string[] parameters)
{
if (entity == null)
return;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(entity.GetType());
serializer.WriteObject(response.Stream, entity);
}
This method takes an object (
entity
) and (provided it’s not null) uses the DataContractJsonSerializer
again to write it to the response stream (which we can access through response.Stream
here). There’s not much more to say here – once again, we’re done!