This is the second part in a series of posts about reducing the amount of data transferred between ASP.NET Web API or Azure Mobile App Service and the (mobile) client.
We will continue where we left off in Part 1: Default Value Handling.
In the first post we managed a reduction of 41%.
Of course, the reduction depends heavily on how often default values are part of your transferred data. But it’s an easy diet on transferred data that the other side can reconstruct on itself.
In this post we will squeeze a little bit more from our Data Transfer Objects (DTOs).
Part 2: Skip empty collections
First, we look at the response we got from the second controller:
(Formatted for readability)
We see that the property SomeEmptyObjects
is present in the message, despite being an empty collection.
So, this is the next target for elimination.
Removing the empty collection
The reason the collection is present in the message is because a value of null
is of course different than an empty array.
But this post is about eliminating data on transport so we start with removing empty collections from our messages.
I was not the first one with this question, and at StackOverflow I found an answer by Athari.
The post contains a custom contract resolver that deals with this situation.
I created a third controller with the custom ContractResolver
.
This controller demonstrates this new behavior.
A GET
request to http://reducejsontraffic.azurewebsites.net/api/skipemptycollection
returns:
(Formatted for readability)
Removing the empty collection removes another 6% from our transferred data. Bringing the total reduction of this example to 47%.
As I wrote before: there is a difference between a value of null
and an empty array.
The trade-off is that by removing the information from the DTO the client doesn’t know if there was an empty collection or nothing at all.
If this is fine for your code you’re done for now.
Reviving empty collections on the receiving side
In the previous blog post I used the DefaultAttribute
to declare these defaults on simple types:
When the client uses the Populate
(or IgnoreAndPopulate
) as DefaultValueHandling
the property Fourteen
will get the value of 14
then it’s not present in the data.
This also works for the AStringArray
property which is an array of String
.
However, for the arrays that contain objects you can’t declare this as a default. When you try:
This will give the following error:
“CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type”
We will have to find another way to declare the default value.
Initialize collections in the constructor
My own preferred way to initialize collections on a POCO is by setting the collection in the constructor.
This will make sure we always have an empty collection. And if there is data in the message the Serializer will create a filled collection.
There is a caveat though. When there is no data in message and we have a set the DefaultValueHandling to Populate
the empty collection is overwritten with a value of null
.
Therefore, we need to override the DefaultValueHandling for collection properties.
To test this, we launch the demo client app again.
When we look at the watch in the debugger, we see again that all the properties that are not present in the transferred data are populated with either null
or their correct default value.
Conclusion
In this example we managed to get a total reduction of 47% and still have all the data available in the client.
Of course, the reduction still depends heavily on how often default values and empty collections are part of your transferred data. But the diet continues.
Source code
You can download the updated Reduce Json Traffic sample project on GitHub.
You can also go to the specific commit to see the exact changes.