Skip to content

Implementing a Codec

idg10 edited this page Jun 19, 2011 · 7 revisions

About Codecs

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:

  [MediaType("application/json;q=0.5", "json")]
  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);
      }
  }

The MediaType attribute

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").

The Configuration implementation

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.

The IMediaTypeReader.ReadFrom method

      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!

The IMediaTypeWriter.WriteTo method

      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!
Clone this wiki locally