Wednesday, November 18, 2009

A light-weight .NET framework for publishing Layar layers using WCF and Unity (C#)

Thus speaks Wikipedia:

Augmented reality (AR) is a term for a live direct or indirect view of a physical real-world environment whose elements are merged with (or augmented by) virtual computer-generated imagery - creating a mixed reality.

Amen. Fact is that AR is currently as hot a nuclear reactor core making its way to China, and that everyone and his aunt are scrambling to get a piece of the action. So why not me ;-)

On November 9th this year, my colleague Jeroen Prins, who did some prototyping with Layar, pushed a HTC Hero in my hands with the words “see if you can do something nice with it”. So in a few evenings I created a little framework for making Layar layers in an easier and consistent way. It is based upon some of Jeroen’s prototype, but since he insisted on not having credits for this I won’t give him any ;-). The framework uses the Enterprise Library, most notably Unity, and I assume you are familiar with it.

Since WCF can be bent in almost every direction as far as generating content is concerned, I decided to use it for my solution. I started, as you always start, with the data contract. The object model is pretty simple: a Layer object has Point-Of-Interest (Poi) objects, a Poi has Action objects. If you study the Layar GetPointsOfInterest page for a few minutes you will see the implementation is WCF 101. Maybe 102 ;-). Contrary to my habits, I forego on the comments – those are all on the GetPointsOfInterest page. First, the Action object:

using System.Runtime.Serialization;

namespace LocalJoost.Layar
{
[DataContract(Name = "Action")]
public class Action
{
[DataMember(Name = "uri")]
public string Uri { get; set; }

[DataMember(Name = "label")]
public string Label { get; set; }
}
}

The member name is “Uri” (following the .NET coding guidelines) but with adding “Name=uri” in the DataMember attribute I tell WCF to serialize the member as “uri”, without the capital “U”, thus following exactly the Layer API description. This is standard WCF stuff. Then, the Poi class:


using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace LocalJoost.Layar
{
[DataContract (Name="POI")]
public class Poi
{
public Poi()
{
Actions = new List();
}
[DataMember(Name = "actions")]
public List Actions { get; set; }

[DataMember(Name = "attribution")]
public String Attribution { get; set; }

[DataMember(Name = "distance")]
public double Distance { get; set; }

[DataMember(Name = "id")]
public string Id { get; set; }

[DataMember(Name = "imageURL")]
public string ImageUrl { get; set; }

[DataMember(Name = "lat")]
public int Latitude { get; set; }

[DataMember(Name = "lon")]
public int Longitude { get; set; }

[DataMember(Name = "line2")]
public string Line2 { get; set; }

[DataMember(Name = "line3")]
public string Line3 { get; set; }

[DataMember(Name = "line4")]
public string Line4 { get; set; }

[DataMember(Name = "title")]
public string Title { get; set; }

[DataMember(Name = "type")]
public int Type { get; set; }
}
}

and finally, the Layer object itself:


using System.Collections.Generic;
using System.Runtime.Serialization;

namespace LocalJoost.Layar
{
[DataContract]
public class Layer
{
public Layer()
{
Hotspots = new List();
}

[DataMember(Name = "nextPageKey")]
public string NextPageKey { get; set; }

[DataMember(Name = "morePages")]
public bool MorePages { get; set; }

[DataMember(Name = "hotspots")]
public List Hotspots { get; set; }

[DataMember(Name = "layer")]
public string LayerName { get; set; }

[DataMember(Name = "errorCode")]
public int ErrorCode { get; set; }

[DataMember(Name = "errorString")]
public string ErrorString { get; set; }
}
}

I move on to the whopping complex service contract:


using System.ServiceModel;
using System.ServiceModel.Web;

namespace LocalJoost.Layar
{
[ServiceContract(Namespace = "www.yournamespacehere.nl/layar")]
public interface ILayarService
{
[OperationContract]
[WebGet(UriTemplate = "Layar/{layerName}/*",
ResponseFormat=WebMessageFormat.Json)]
Layer GetLayerData(string layerName);
}
}

which defines the output for this as being JSON, and a custom URI matching pattern which allows us to put the actual layer name in the URL. The * at the end means "and the rest is also accepted". Now the title of this posting says I was doing something with Unity, and here it comes: I define an equally complex interface for a Layar "provider" which will be used by the service implementation:


using System.Collections.Generic;

namespace LocalJoost.Layar
{
public interface ILayarProvider
{
Layer Get(double? lat, double? lon,
int? radius, int? accuracy,
IDictionary requestParameters);
}
}

The final piece of real code is the implementation of the ILayarService service contract, with apologies for the crappy layout, but some WCF class names are a wee bit long:


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.ServiceModel.Web;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using LocalJoost.Utilities.Unity;

namespace LocalJoost.Layar
{
/// <summary>
/// Layar service implementation
/// </summary>
public class LayarService : ILayarService
{
/// <summary>
/// Request parameters
/// </summary>
private static NameValueCollection RequestParams
{
get
{
return WebOperationContext.Current != null ? WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters
: null;
}
}

private static readonly List<string> KeyWordsProcessed =
new List<string> { "lat", "lon", "radius", "accuracy" };

/// <summary>
/// Gets the layer data.
/// </summary>
/// <param name="layerName">Name of the layer.</param>
/// <returns></returns>
public Layer GetLayerData(string layerName)
{
try
{
if (WebOperationContext.Current != null )
{
Logger.Write("Layar call: " + WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri);
}
// Note: layername is lowercase
var provider =
new UnityResolver(
layerName.ToLowerInvariant()).Resolve<ILayarProvider>();

// Collect the other parameters
var reqParms = new Dictionary<string, string>();
foreach( var key in RequestParams.Keys)
{
var keyS = key.ToString();
if (!KeyWordsProcessed.Contains(keyS))
reqParms.Add(keyS, RequestParams[keyS]);
};

return provider.Get(
GetRequestDouble("lat"), GetRequestDouble("lon"),
GetRequestInt("radius"), GetRequestInt("accuracy"),
reqParms);
}
catch( Exception ex)
{
Logger.Write(ex,"Exceptions");
return null;
}
}

#region Utility methods
private double GetRequestDouble(String keyname)
{
if (!(RequestParams == null ||
string.IsNullOrEmpty(RequestParams[keyname])))
{
return Convert.ToDouble(RequestParams[keyname],
CultureInfo.InvariantCulture);
}
return -1;
}

private int GetRequestInt(String keyname)
{
if (!(RequestParams == null ||
string.IsNullOrEmpty(RequestParams[keyname])))
{
return Convert.ToInt32(RequestParams[keyname],
CultureInfo.InvariantCulture);
}
return -1;
}
#endregion
}
}

Here comes my little UnityResolver into play, which was described earlier in this blog. What this LayarService basically does is accept a layer name, burp the call into a log file, get the lat, lon, radius, and accuracy from the query string, dump the rest of the query string parameters into a dictionary, use Unity to determine which ILayerProvider implementation is to be loaded, call it’s Get method with the collected data, and return the result.


Now there are only six steps to make this actually work. First, you define a web application project. You reference the LocalJoost.Layar project, System.ServiceModel.dll, System.Runtime.Serialization, every file in the Enterprise Library that starts with "Microsoft.Practices.Unity" (I have 5), and Microsoft.Practices.EnterpriseLibrary.Logging.dll.


The second step is: add a text file to the web application, for instance "LayarService.txt". You enter the following text in it:


<%@ ServiceHost Language="C#" 
Debug="true" Service="LocalJoost.Layar.LayarService" %>

and rename this the file to "LayerService.svc". The third step is some WCF configuration in the web.config of your web application to host the service as a webHttpBinding, thus making it accept calls via http get:


<services>
<service name="LocalJoost.Layar.LayarService">
<endpoint address="" binding="webHttpBinding"
behaviorConfiguration="WebHttpBehavior"
contract="LocalJoost.Layar.ILayarService"
bindingNamespace="http://whatever/layar">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="WebHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>

The fourth step is to map your implementations of ILayarProvider to your layers. The LayerService class works in such a way that a layer maps directly to a Unity container, so a configuration might look like this:


<unity>
<typeAliases>
<typeAlias alias="ILayarProvider"
type="LocalJoost.ILayarProvider,LocalJoost.Layar"/>
<typeAlias alias="SampleProvider"
type="SomeAssembly.SampleProvider,SomeAssembly"/>
<typeAlias alias="AnotherProvider"
type="SomeotherAssembly.AnotherProvider, SomeotherAssembly"/>
</typeAliases>
<containers>
<container name="mylayer">
<types>
<type type="ILayarProvider"
mapTo="SampleProvider"/>
</types>
</container>
<container name="someotherlayer">
<types>
<type type="ILayarProvider"
mapTo="AnotherProvider"/>
</types>
</container>
</containers>
</unity>

Here I have defined two sample containers, thus layers. If, for instance, your service is hosted as “http://mydomain.com/LayarServer/LayerService.svc” you can register your Layer with the Layar developer portal (which is, incidentally, the fifth step) as “http://mydomain.com/LayarServer/LayerService.svc/Layar/mylayer/” (mind the trailing slash!) and the framework will do the rest.


Now the sixth and final step is the real hard part: writing actual implementations of the ILayarProvider. This includes coordinate system conversions, as well as creating, converting and querying all kinds of spatial data sets. And this is, dear reader, what me and my employer Vicrea are perfectly willing to help you with. Handling spatial information and making cutting-edge applications with it is exactly our prime specialty. You will find our rates to be quite reasonable ;-)


And by the way – if you are a developer and like to work on this kind of software, please drop me a note. We are hiring!

A simple Unity helper for making class decoupling easier

Unity, a part of the Enterprise Library, is a great way of decoupling interfaces and implementation, and for opening a road into unit testing with mock objects. Unfortunately, using it can be a bit cumbersome. You have to create a container, configure it from code or from configuration, and have to decide when to do what. I thought it could do with some help, so I created some helper classes. I must admit this inspired by The Common Service Locator and some code for that I got from Dennis van der Stelt, but it is a little simpler to use. And it works only with Unity ;-) The idea is to define a factory and a resolver, that know of each other only trough an interface:

namespace LocalJoost.Utilities.Unity
{
public interface IUnityResolver
{
Resolve<T>();
}
}

Then, I define a UnityResolver, which is basically a wrapping around a UnityContainer class:

using System;
using System.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

namespace LocalJoost.Utilities.Unity
{
/// <summary>
/// Base class for unity resolvers
/// </summary>
[Serializable]
public class UnityResolver : IUnityResolver
{
protected static string _defaultContainer = "Default";
protected IUnityContainer Container{get; set;}

/// <summary>
/// Initializes a new instance of the UnityResolver
/// Override this constructor if you want to write your own default
/// behaviour.
/// Register in code by adding lines like:
/// Container.RegisterType(Type.GetType("NameSpace.IMyClass",true),
/// Type.GetType("NameSpace.MyClass",true));
/// </summary>
public UnityResolver()
: this(_defaultContainer)
{
}

/// <summary>
/// Initializes a new instance of the UnityResolver class.
/// </summary>
/// <param name="containerName">Name of the container.</param>
public UnityResolver(string containerName)
{
Container = new UnityContainer();

var section = ConfigurationManager.GetSection("unity")
as UnityConfigurationSection;
if (section != null)
{
var containerConfiguration = section.Containers[containerName];
if (containerConfiguration != null)
{
section.Containers[containerName].Configure(Container);
}
}
}

/// <summary>
/// Resolves an instance of T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Resolve<T>()
{
return Container.Resolve<T>();
}
}
}

You can use this class directly, by calling new UnityResolver(“MyContainer”).Resolve<IMyType>(). The UnityResolver looks for a Unity container named “Default” in your configuration file. If that is not present, it creates an empty container. Use of the latter feature is described below.


This is not very efficient when all your classes are sitting into one and the same container, and you may want to have some consistent behavior of your classes during unit testing with mockups. So I created the UnityFactory class that can accept a resolver and hold it:


using System;
using System.Configuration;
using System.Reflection;
using System.Web;

namespace LocalJoost.Utilities.Unity
{
/// <summary>
/// Static helper class for shortcutting Unity instantiated
/// classes
/// </summary>
public class UnityFactory
{
/// <summary>
/// Method to set the resolver manually - use this for unit testing
/// </summary>
/// <param name="resolver">The resolver.</param>
public static void SetResolver( IUnityResolver resolver)
{
Resolver = resolver;
}

/// <summary>
/// Gets a resolver from configuration.
/// </summary>
/// <returns></returns>
private static IUnityResolver GetResolverFromConfiguration()
{
var configuredDefaultResolver =
ConfigurationManager.AppSettings["UnityResolver"];
if (!string.IsNullOrEmpty(configuredDefaultResolver))
{
var specParts = configuredDefaultResolver.Split(',');
var ass = Assembly.Load(specParts[1]);
var objType = ass.GetType(specParts[0]);
return Activator.CreateInstance(objType) as IUnityResolver;
}
return null;
}

/// <summary>
/// Gets the instance of an object via an interface
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetInstance<T>()
{
// First, make sure there is a resolver.
// If none is defined, try to load one from configuration
// If that fails too, use the default resolver
if (Resolver == null)
{
Resolver = GetResolverFromConfiguration() ?? new UnityResolver();
}

// Then, resolve the interface to an object instance
return Resolver.Resolve<T>();
}

#region Properties
/// <summary>
/// Gets or sets the resolver. Uses Http context or static variable
/// to store a created resolver
/// </summary>
/// <value>The resolver.</value>
private static IUnityResolver Resolver
{
get
{
if (HttpContext.Current == null)
{
return _resolver;
}
return HttpContext.Current.Application["__UnityResolver"]
as IUnityResolver;
}

set
{
if (HttpContext.Current == null)
{
_resolver = value;
}
else
{
HttpContext.Current.Application["__UnityResolver"] = value;
}
}
}

private static IUnityResolver _resolver;
#endregion
}
}

Usage of this class is UnityFactory.Resolve<IMyType>(). And presto, you have got a reference to your implementation class. That’s all there is. Except for some configuration, of course ;-).


Another feature of this class is that it looks for a config setting “UnityResolver”. If that is present, it tries to load that class for a resolver in stead of the default UnityResolver. For instance, you can subclass UnityResolver, override the default constructor and define your interface-to-implementation mapping in code. Now this may look strange, because what is the point of decoupling classes and then make the mappings in code again? Well, for a scenario in which you want to use Unity for unit testing with mockups, this makes sense – when you want to deliver your production application without the necessity for a (possible very large) unity mapping section in your configuration file. If you want to register objects from code, you can do it for instance like this in the constructor of your UnityResolver override:


Container.RegisterType(
Type.GetType("SomeNamespace.IMyInterface,SomeNamespace", true),
Type.GetType("SomeOtherNamespace.MyImplementation,SomeOtherNamespace", true));

where "SomeNamespace" and "SomeOtherNamespace" after the comma are assembly names. If you use this construct, because it looks nice in NDepend and you don't have to make references, make sure your set the second parameter of Type.GetType, throwOnError, to true or it will fail silently and you might spend an uncomfortable long time debugging (I am talking from experience here). Personally I would go for typeof in stead of using Type.GetType but that is a matter of taste.


As a last feature, in unit testing scenarios, you can make another subclass of UnityResolver, call UnityFactory.SetResolver(myResolver) and the UnityFactory will store your resolver in a static variable (or the application context). Subsequently, all your classes will use the mapping logic defined in your own resolver, which makes a great starting point for mockup testing.


I hope this little sample will make decoupling, unit testing and mockup objects using Unity a bit more accessible.

Saturday, October 17, 2009

Using the Microsoft Ajax library to keep the javascript "this" context intact

This is not strictly .NET but more of a Javascript trick, but nevertheless, one that you might need. It is a simple trick, and actually requires much more text to decribe the problem than the actual solution ;-)

Javascript supports the notion of object oriented programming, to an extent. There's plenty to find about that on the web and I don't care to repeat that. One of the more annoying things of Javascript is that it does support callbacks, but is quite careless about object context. This happens for instance when you use callbacks to process data coming back from calling WCF services. Suppose, for instance, I take my project in which I explain how to get data from WCF services in Javascript and rewrite the Javascript code as an object:

function CallBackProcessor()
{
this.CallSingle = function()
{
dotnetbyexample.JSONDemoService.IJSONService.GetSingleObject(
document.getElementById("tbId").value,
document.getElementById("tbName").value,
this.CallBackSingle);
}
this.CallBackSingle = function(WebServiceResult)
{
resultDiv = document.getElementById("result1");
resultDiv.innerHTML = this.CreateMessage(WebServiceResult);
}
this.CreateMessage = function(WebServiceResult)
{
return( "You entered: id = " +
WebServiceResult.Id + " name = " +
WebServiceResult.Name);
}
}
var processor = new CallBackProcessor();
and then change the code of the button calling the service to call the processor object
<asp:Button ID="btnSelect" runat="server" Text="SingleSelect" 
UseSubmitBehavior="False"
OnClientClick="processor.CallSingle(); return false;" />
I will get the notorious "Object doesn't support this property or method" error. Why? Because I try to call the method "CreateMessage" from the "CallBackSingle": the context of "this" is no longer that of "processor", but of whatever is calling the callback. You can find this out by placing a breakpoint before the call to CreateMessage and then view the properties of "this". The logic behind this behaviour defies my imagination, but the solution is actually pretty simple: use Function.createDelegate to set up the callback in stead of calling it directly
this.CallSingle = function()
{
dotnetbyexample.JSONDemoService.IJSONService.GetSingleObject(
document.getElementById("tbId").value,
document.getElementById("tbName").value,
Function.createDelegate(this, this.CallBackSingle));
}
And that's all there is to it. The "this" context stays intact, thanks to the Microsoft Ajax library, that you already included automatically by placing a ScriptManager on your page.

Tuesday, August 25, 2009

Yet another way to determine root paths in ASP.NET

How many times I have tried to find the root path of an ASP.NET page or a handler (ASHX) in order to call another URL I can hardly count. When working with WMS and WFS services and connected documents - when you call all kind of weird 'services' - it's something that needs to be done often. I decided to put a few in what seems to become my trade mark - extension methods.

I created the following extensions to HttpContext

using System;
using System.Web;

namespace LocalJoost.Utilities.Web
{
public static class HttpContextExtensions
{
public static string FullRootPath( this HttpContext context )
{
return context.Request.Url.AbsoluteUri.Split(
new[] { context.Request.Url.AbsolutePath },
StringSplitOptions.RemoveEmptyEntries)[0];
}

public static string FullApplicationPath(this HttpContext context)
{
return string.Concat(context.FullRootPath(),
context.Request.ApplicationPath);
}
}
}
In the Page_Load of an aspx page you can, for instance, use the methods as described below:
var r = Context.FullRootPath();
var a = Context.FullApplicationPath();
r will contain something like "http://localhost:3974" and a "http://localhost:3974/YourTestSite". Maybe there are simpler ways, but these work very well for me.

Friday, August 21, 2009

Azure Tables and DateTime: some limitations

A nice thing I ran into yesterday: if you have a TableStorageEntity child class containing a DateTime property, you have two thing to remember:

  • You cannot use DateTime.Now, just should use DateTime.UtcNow when defining "now"
  • Make sure your DateTime does default to TableStorageConstants.MinSupportedDateTime (and not the standard DateTime.MinValue)
I had a class like this
using System;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace LocalJoost.CloudMapper.Data.MapBuild
{
public class Job : TableStorageEntity
{
#region Properties
public string JobId { get; set; }
public string MapName { get; set; }

public DateTime QueuedAt { get; set; }
public DateTime StartedAt { get; set; }
public DateTime FinishedAt { get; set; }
#endregion

public Job()
{
QueuedAt = DateTime.Now;
}

public override string PartitionKey
{
get{return MapName;}
set{}
}

public override string RowKey
{
get{return JobId;}
set
{ }
}
}
}
and I simply could not store it. "There was an error processing the request". Yeah. I was able to find out the restrictions mentioned above, changed the class to
using System;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace LocalJoost.CloudMapper.Data.MapBuild
{
public class Job : TableStorageEntity
{
#region Properties
public string JobId { get; set; }
public string MapName { get; set; }

public DateTime QueuedAt { get; set; }
public DateTime StartedAt { get; set; }
public DateTime FinishedAt { get; set; }
#endregion

public Job()
{
QueuedAt = DateTime.UtcNow;
StartedAt = TableStorageConstants.MinSupportedDateTime;
FinishedAt = TableStorageConstants.MinSupportedDateTime;

}

public override string PartitionKey
{
get{return MapName;}
set{}
}

public override string RowKey
{
get{return JobId;}
set
{ }
}
}
}
and then it worked.

Thanks to Steve Marx for pointing me to the TableStorageConstants on twitter live while I was debugging

Saturday, August 15, 2009

Using extension methods to serialize objects to XML and compress the result - and deserialize again

Elaborating upon the string extension methods I created for compressing and decompressing strings to and from a byte array, it turned out fairly easy to create another set of compress / decompress methods to serialize any type of object to and from a byte array containing compressed XML.

I once again took the StringZipExtensions class and added the following methods to compress any old object to a byte array:

/// <summary>
/// XmlSerializes the object to a compressed byte array
/// using the specified encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress,
Encoding encoding)
{
var xmlSerializer = new XmlSerializer(objectToCompress.GetType());
using (var stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, objectToCompress);
return stringWriter.ToString().Compress(encoding);
}
}

/// <summary>
/// XmlSerializes the object to a compressed byte array using default
/// UTF8 encoding.
/// </summary>
/// <param name="objectToCompress">The object to compress.</param>
/// <returns>bytes array with compressed serialized object</returns>
public static byte[] Compress(this object objectToCompress)
{
return Compress(objectToCompress, new UTF8Encoding());
}
Here, once again, an overload using a default UTF8 encoding and a method which uses your own encoding to do the heavy lifting. Then, the methods for decompressing, which - in all modesty - are a rather neat usage of generics, I think:
/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using the specified encoding
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="compressedObject">The compressed string.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject,
Encoding encoding)
{
var xmlSer = new XmlSerializer(typeof(T));
return (T)xmlSer.Deserialize(new StringReader(
compressedObject.DecompressToString(encoding)));
}

/// <summary>
/// Decompress an array of bytes into an object via Xml Deserialization
/// using default UTF8 encoding
/// </summary>
/// <param name="compressedObject">The compressed string.</param>
/// <returns>Decompressed object</returns>
public static T DecompressToObject<T>(this byte[] compressedObject )
{
return DecompressToObject<T>(compressedObject, new UTF8Encoding());
}
Then you can take something like this hugely complex ;-) object Person
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }

public override bool Equals(object obj)
{
var toCompare = obj as Person;
if( toCompare != null )
{
return
Name.Equals(toCompare.Name) &&
Id.Equals(toCompare.Id) &&
Birthday.Equals(toCompare.Birthday);

}
return base.Equals(obj);
}
}
and serialize/compress and decompress/deserialize it like this:
[Test]
public void CompressObjectTest()
{
var baseLineObject = new Person
{
Id = 99,
Name = "Tom",
Birthday = new DateTime(1969, 06, 03)
};
var compressed = baseLineObject.Compress();
var testObject = compressed.DecompressToObject<Person>();
Assert.AreEqual(testObject, baseLineObject);
}
Code downloadable here

Wednesday, August 12, 2009

Using extension methods to compress and decompress strings

After some rightful comments by Jarno Peschier I decided to once again look into my blog post about putting compressed object on the Azure message queue and came up with the following set of extensions methods that allow you to compress a string via gzip into a byte array, and unzip a byte array containing a compressed string to a regular string again:

using System.IO;
using System.IO.Compression;
using System.Text;

namespace LocalJoost.Utilities.Compression
{
public static class StringZipExtensions
{
/// <summary>
/// Compresses the specified string a byte array using the specified
/// encoding.
/// </summary>
/// <param name="stringToCompress">The string to compress.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>bytes array with compressed string</returns>
public static byte[] Compress(
this string stringToCompress,
Encoding encoding )
{
var stringAsBytes = encoding.GetBytes(stringToCompress);
using (var memoryStream = new MemoryStream())
{
using (var zipStream = new GZipStream(memoryStream,
CompressionMode.Compress))
{
zipStream.Write(stringAsBytes, 0, stringAsBytes.Length);
zipStream.Close();
return (memoryStream.ToArray());
}
}
}

/// <summary>
/// Compresses the specified string a byte array using default
/// UTF8 encoding.
/// </summary>
/// <param name="stringToCompress">The string to compress.</param>
/// <returns>bytes array with compressed string</returns>
public static byte[] Compress( this string stringToCompress )
{
return Compress(stringToCompress, new UTF8Encoding());
}

/// <summary>
/// Decompress an array of bytes to a string using the specified
/// encoding
/// </summary>
/// <param name="compressedString">The compressed string.</param>
/// <param name="encoding">The encoding.</param>
/// <returns>Decompressed string</returns>
public static string DecompressToString(
this byte[] compressedString,
Encoding encoding)
{
const int bufferSize = 1024;
using (var memoryStream = new MemoryStream(compressedString))
{
using (var zipStream = new GZipStream(memoryStream,
CompressionMode.Decompress))
{
// Memory stream for storing the decompressed bytes
using (var outStream = new MemoryStream())
{
var buffer = new byte[bufferSize];
var totalBytes = 0;
int readBytes;
while ((readBytes = zipStream.Read(buffer,0, bufferSize)) > 0)
{
outStream.Write(buffer, 0, readBytes);
totalBytes += readBytes;
}
return encoding.GetString(
outStream.GetBuffer(),0, totalBytes);
}
}
}
}

/// <summary>
/// Decompress an array of bytes to a string using default
/// UTF8 encoding.
/// </summary>
/// <param name="compressedString">The compressed string.</param>
/// <returns>Decompressed string</returns>
public static string DecompressToString(this byte[] compressedString )
{
return DecompressToString(compressedString, new UTF8Encoding());
}
}
}
You can now quite simply do something like
using LocalJoost.Utilities.Compression;
using NUnit.Framework;

namespace LocalJoost.Utilities.Test
{
[TestFixture]
public class TestCompress
{
[Test]
public void TestZipUnzip()
{
const string test = "The quick brown fox jumps over the lazy dog";
var compressed = test.Compress();
var uncompressed = compressed.DecompressToString();
Assert.AreEqual(test, uncompressed);
}
}
}
and that will work, of course ;-)

Both the Compress and the DecompressToString methods use UTF8 as default encoding as Jarno suggested by Live Messenger, but both methods also sport an overload which allow you to provide an encoding yourself. This will support almost any encoding - provided of course you use the same encoding to compress and decompress. So, this can even be used to compress strings containing Klingon messages - I hope this will earn me a fond majQa' from Jarno ;-)

Code downloadable here