How to make ASP.NET WebApi serialize your LLBLGen Pro entities to JSON

LLBLGen Pro has several ways to serialize / deserialize your entity data in an LLBLGen Pro Entity object:

  • Binary, using superfast custom binary formatters, triggered by ISerializable methods.
  • XML, using XmlDocuments
  • XML, using XmlWriters, triggered by IXmlSerializable methods. Xml is configurable to various formats, from verbose (to rebuild the objects using the embedded type info) to compact ("Just the data, ma'am")

Once upon a time, all these fancy methods were the latest and greatest. Today however, they're not modern anymore, as a new format is taken over: JSON. JSON as the advantage that you can easily consume it in javascript on the client, no custom XML parser needed.

Microsoft recently released ASP.NET 4.0 MVC with a fancy new service system: WebAPI. WebAPI let's you create a service without the hassle that's required with a WCF service running inside IIS. Not only makes WebAPI it easy to write a service for your data needs on a client, it also makes it easy to serialize/deserialize this data as JSON.

This post is about how to make WebAPI work with LLBLGen Pro and serialize the data to JSON without the internals bleeding into the JSON data.

Note: by following these steps, the data is serialized as JSON only, the XML serializer won't work anymore (see below). By default, WebAPI serializes to XML, so you need these steps only if you want to serialize to JSON.

Step 1: decorate your entities, fields and navigators

LLBLGen Pro's designer has the ability to decorate generated classes and members of the generated classes with attributes, even based on rules you set. See a previous blogpost about this feature or the documentation for details.

Load your LLBLGen Pro project into the designer and select from the main menu Project -> Settings. A dialog opens. Navigate to General -> Conventions -> Code Generation -> Attributes.

At the top, 'Entity' is selected. Add below Attribute definition: DataContract

Press enter.

Select at the top 'NormalField'. Add below Attribute definition: DataMember

Press enter.

Repeat the action you did with NormalField also for NavigatorSingleValue and NavigatorCollection.

Click OK to close the settings dialog. You have now decorated all entities, the fields of the entities and the navigator properties with the required attributes.

Regenerate your code.

Step 2: Make WebAPI's JSON serialize do the right thing

Now, you might think that with the DataContract/DataMember attributes, we're going to use the DataContractJsonSerializer, but that's not the case. Not only is that serializer not recommended by many, including Rick Strahl, it also fails to do anything as it complains that the entity objects implement IXmlSerializable and it can't work with objects which implement IXmlSerializable and have DataContract attributes. These kind of errors show how dumb the serialization strategies (or lack thereof, if they had implement a proper strategy pattern for this it would all be OK) of Microsoft have been during the years: I have to have IXmlSerializable implemented on the classes for Xml serialization over normal WCF services to make sure change tracking data is preserved and also object graph references are left in-tact. The same error is given when we try to serialize the data to XML instead of JSON.

We're going to use the default JSON serializer of WebAPI, which is the Newtonsoft JSON.NET serializer. By default it doesn't do what we want, so we have to configure it. It's simple:

Open WebApiConfig.cs which is located in the App_Start folder. Now add the following lines to the Register method:

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
json.SerializerSettings.ContractResolver = new DefaultContractResolver() 
    { 
        IgnoreSerializableInterface = true, 
        IgnoreSerializableAttribute = true 
    };

Step 3: There's no step 3

You're all setup now. A method like:

public IEnumerable<ProductEntity> GetAllProducts()
{
    using(var adapter = new DataAccessAdapter())
    {
        var metaData = new LinqMetaData(adapter);
        return metaData.Product.WithPath(p=>p.Prefetch(x=>x.Category)).ToList();     
    }
}

will now properly serialize the data (All product entities and their related category entities) to JSON, including reference tracking. Again, pulling the data as XML by specifying application/xml in the Accept header element of the request will make things fail as WebAPI chooses a DataContract serializer by default if it sees a [DataContract] attribute on the objects to serialize. It might be configurable to have it choose to use the normal XmlSerializer instead, but I'm no WebAPI expert so I can't give you a heads-up on that. If you know whether this is possible, please leave a comment below :)

Hope this helps!

Published Friday, October 5, 2012 3:01 PM by FransBouma

Comments

# re: How to make ASP.NET WebApi serialize your LLBLGen Pro entities to JSON

Saturday, October 6, 2012 3:14 PM by mattjcowan

This is great, I can't believe I haven't tried this. I've been using custom templates to generate DTO classes up till now, which works nicely with the off-the-shelf data contract serializer. I also like to use ServiceStack.Text for serialization - json.net is also good. Anyhow, I'll definitely give this a go.

# re: How to make ASP.NET WebApi serialize your LLBLGen Pro entities to JSON

Wednesday, October 10, 2012 11:20 AM by Ryan

I'm getting a "System.Runtime.Serialization.SerializationException: Member '_fieldsData' was not found." exception when I try to deserialize on the client side.  Any idea what I might be doing wrong?

# re: How to make ASP.NET WebApi serialize your LLBLGen Pro entities to JSON

Wednesday, October 10, 2012 11:38 AM by FransBouma

@Ryan: use fiddler and make sure you have the proper settings set as described above so the json is as simple as you'd expect (i.e.: there's no _fieldData, but just field properties).

So you have to make sure the datamember/datacontract attributes are present on your fields/entities (do that in the designer, re-generate code) and make sure the json.net serializer is setup as I described in the code above. Otherwise the json.net serializer will simply call the ISerializable interface member and indeed you'll get _fielddata and a lot of other data you don't need.