C#, Consuming anonymous types at a later point

Last night I was dealing with anonymous types and some IL code to creating and assigning values to instances of anonymous types. I stumbled upon this question:

How do I declare a Field with Anonymous Type (C#)

- http://stackoverflow.com/questions/964334/how-do-i-declare-a-field-with-anonymous-type-c

It didn’t have anything to do with what I was doing but the thing I was doing would solve the issue. I was bringing anonymous types support to ServiceStack.Text, the .Net communities most awesome Serialization framework. Yes I know that the feature already exists in JSON.Net, but when it comes to serialization, I want speed, hence why I turn to ServiceStack.

Alternative 1 – Lets solve the problem using JSON

Step 1 – Install ServiceStack.Text v3.3.6 or later

This feature was introduced in v3.3.6, so use that NuGet.

install-package ServiceStack.Text

Step 2 – Get some data

For this simple demo I will just use some simple person entities that gets yielded.

public class Person
{
	public string Firstname { get; set; }
	public string Lastname { get; set; }
	public int Age { get; set; }
}

public static class Db
{
	public static IEnumerable<Person> Persons()
	{
		yield return new Person
		{
			Firstname = "Hans",
			Lastname = "Wertheim", 
			Age = 30
		};
		yield return new Person
		{
			Firstname = "Daniel", 
			Lastname = "Wertheim", 
			Age = 31
		};
		yield return new Person
		{
			Firstname = "Anton",
			Lastname = "Wertheim", 
			Age = 32
		};
	}
}

Step 3 – Turn it into JSON

Now we need the state turned into JSON. If we want we could transform the data now. Lets combine Firstname and Lastname to Name.

var personsAsJson = Db.Persons()
	.Select(p => new
	{
		p.Age, 
		Name = string.Concat(p.Firstname, " ", p.Lastname)
	})
	.Select(JsonSerializer.SerializeToString)
	.ToArray();

Step 4 – Define a extension method for deserialization

Once we have the JSON blob, we need a deserialization method allowing us to define a anonymous type, used as a template.

public static IEnumerable<T> ToAnonymousType<T>(
	this IEnumerable<string> json, T template) where T : class
{
	TypeConfig<T>.EnableAnonymousFieldSetters = true;
	var templateType = template.GetType();
	return json.Select(j => JsonSerializer.DeserializeFromString(j, templateType) as T);
}

Step 5 – Consume it as we want in another domain

Now, in another domain we could just turn it into a anonymous type of our like. E.g only picking out Name.

var anonymousPersons = personsAsJson.ToAnonymousType(new {Name = default(string)});

foreach (var anonymousPerson in anonymousPersons)
	Console.WriteLine(anonymousPerson.Name);

That’s it. No classes (other than anonymous) used in the tranformations what so ever. Not saying you should, just saying you could.

//Daniel

Json.Net vs ServiceStack.Text

This is going to be a verry short comparision of the two Json-serialization frameworks:
Json.Net – http://json.codeplex.com
ServiceStack.Text – https://github.com/mythz/ServiceStack.Text

I have always used Json.Net when dealing with serialization of entities back and forth to Json, but then I stumbled upon this: “Fastest JSON Serializer for .NET released” – http://www.servicestack.net/mythz_blog/?p=344 Man was I excited, since I need the “best” performance I can get for this in SisoDb (http://www.sisodb.com)

Lets do a simple test

I just downloaded latest version of both libraries (Json.Net – changeset: 57577), (ServiceStack.Text – changeset: c3e5d0c, v1.8), compiled them for .Net 4.0 Client profile and put together a very simple example. Note, I’m not testing features here, just raw serialization and deserialization. I don’t know if you can “tweak” ServiceStack.Text as much as you can with Json.Net, e.g: Strategies for null property handling, missing member, indentation of Json etc.

Item being serialized

[Serializable]
public class Customer
{
    public int Id { get; set; }

    public int CustomerNo { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public ShoppingIndexes ShoppingIndex { get; set; }

    public DateTime CustomerSince { get; set; }

    public Address BillingAddress { get; set; }

    public Address DeliveryAddress { get; set; }

    public Customer()
    {
        ShoppingIndex = ShoppingIndexes.Level0;
        BillingAddress = new Address();
        DeliveryAddress = new Address();
    }
}

[Serializable]
public class Address
{
    public string Street { get; set; }

    public string Zip { get; set; }

    public string City { get; set; }

    public string Country { get; set; }

    public int AreaCode { get; set; }
}

[Serializable]
public enum ShoppingIndexes
{
    Level0 = 0,
    Level1 = 10,
    Level2 = 20,
    Level3 = 30
}

Timer application

static void Main(string[] args)
    {
        var numberOfCustomers = 100000;
        var numberOfItterations = 5;

        //Console.WriteLine("Json.Net");
        //TimeSerializationAction(
        //  SerializeUsingJsonNet, numberOfCustomers, numberOfItterations);
        //TimeDeserializationAction(
        //  Newtonsoft.Json.JsonConvert.SerializeObject, DeSerializeUsingJsonNet,
        //  numberOfCustomers, numberOfItterations);

        //Console.WriteLine("ServiceStack.Text");
        //TimeSerializationAction(
        //  SerializeUsingServiceStackText, numberOfCustomers, numberOfItterations);
        //TimeDeserializationAction(
        //  ServiceStack.Text.JsonSerializer.SerializeToString, DeserializeUsingServiceStackText, 
        //  numberOfCustomers, numberOfItterations);

        Console.ReadKey();
    }

    private static void SerializeUsingJsonNet(IEnumerable<Customer> customers)
    {
        var json = customers.Select(Newtonsoft.Json.JsonConvert.SerializeObject).ToList();
    }

    private static void DeSerializeUsingJsonNet(IEnumerable<string> json)
    {
        var customers = json.Select(
            Newtonsoft.Json.JsonConvert.DeserializeObject<Customer>).ToList();
    }

    private static void SerializeUsingServiceStackText(IEnumerable<Customer> customers)
    {
        var json = customers.Select(ServiceStack.Text.JsonSerializer.SerializeToString).ToList();
    }

    private static void DeserializeUsingServiceStackText(IEnumerable<string> json)
    {
        var customers = json.Select(
            ServiceStack.Text.JsonSerializer.DeserializeFromString<Customer>).ToList();
    }

    private static void TimeSerializationAction(
        Action<IList<Customer>> action, int numOfCustomers, int numOfItterations)
    {
        var stopWatch = new Stopwatch();

        for (var c = 0; c < numOfItterations; c++)
        {
            var customers = CustomerFactory.CreateCustomers(numOfCustomers);

            stopWatch.Start();
            action(customers);
            stopWatch.Stop();

            Console.WriteLine("TotalSeconds = {0}", stopWatch.Elapsed.TotalSeconds);

            stopWatch.Reset();
        }
    }

    private static void TimeDeserializationAction(
        Func<Customer, string> serializer, Action<IList<string>> action, int numOfCustomers, int numOfItterations)
    {
        var stopWatch = new Stopwatch();

        for (var c = 0; c < numOfItterations; c++)
        {
            var customerJsons = CustomerFactory.CreateCustomers(numOfCustomers).Select(serializer).ToList();

            stopWatch.Start();
            action(customerJsons);
            stopWatch.Stop();

            Console.WriteLine("TotalSeconds = {0}", stopWatch.Elapsed.TotalSeconds);

            stopWatch.Reset();
        }
    }
}

Results

Each step: Serialization and Deserialization; has been executed one scenario at a time for each framework. Doing serialization in one execution and deserialization in another. Each time five itterations with 100000 customers in each itteration.

Scenarios: Serialization and Deserialization for:

  • Running debug mode with debugger – (This value is not representative since this isn’t how an application will be deployed)
  • Running debug mode without debugger – (This value is not representative since this isn’t how an application will be deployed)
  • Running release mode without debugger – (These are the measurements I would use for comparison, since this is how my deployed application would run)

Measurements

Json.Net vs ServiceStack.Text - Values

Average values

Json.Net vs ServiceStack.Text - Average values

Now you can decide for your self. I will continue with ServiceStack.Text, especially since deserialization is of greater importance for my project: SisoDb.

Download the example code and Excel with the results.

//Daniel