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!