Cannot return JToken from WCF Web Service

This blog entry is mostly for documentation purposes, so I don’t forget what happened.

TL;DR: WCF’s DataContractJsonSerializer cannot deal with JToken or JObject. If you attempt to include those in your data contract, you will get InvalidDataContractException at runtime, and zero bytes response.

I have a WCF web service written in C# that returns an object with a bunch of strings, along the lines of

[DataContract]
class MyData
{
    [DataMember] public string FirstName { get; set; }
    [DataMember] public string LastName { get; set; }
    [DataMember] public string Addresses { get; set; }
}

Internally it is represented as a name-value bag in some storage. Over time I ended up storing structured data in some values, e.g. Addresses is an array of records encoded in JSON, along the lines of

[{"Where":"1 Main St.","From":"2014-02-01", "To":"2015-07-11"},{"Where":"Under a bridge","From":"2015-07-12"}]

The actual data is not addresses, but the idea is the same.

I use [WebInvoke(..., ResponseFormat=WebMessageFormat.Json] on the service method to make WCF serialize the response as JSON. Left as is, it comes out on the wire like this:

{"FirstName":"Vasya","LastName":"Pupkin","Addresses":"[{\"Where\":\"1 Main St.\", \"From\":\"2014-02-01\",\"To\":\"2015-07-11\"},{\"Where\":\"Under a bridge\",\"From\":\"2015-07-12\"}]"}

Note all the escaped quotation marks. I wanted it to come out as a normal JSON sub-object, like so:

{"FirstName":"Vasya","LastName":"Pupkin","Addresses":[{"Where":"1 Main St.","From":"2014-02-01",
"To":"2015-07-11"},{"Where":"Under a bridge","From":"2015-07-12"}]}

So, I changed the data contract to include JToken and assigned addresses:

[DataContract]
class MyData
{
    [DataMember] public string FirstName { get; set; }
    [DataMember] public string LastName { get; set; }
    [DataMember] public JToken Addresses { get; set; }
}

This compiled and passed tests, but when put to actual use it resulted in an exception during response generation:

System.Runtime.Serialization.InvalidDataContractException: Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'Newtonsoft.Json.Linq.JToken' to remove references to itself.

The client did not get any response at all. It looks like I am not the first one to encounter this problem:

http://stackoverflow.com/questions/12916865/how-to-have-a-wcf-datacontract-with-a-json-dynamic-member
http://stackoverflow.com/questions/30759607/self-hosted-wcf-rest-service-error-type-newtonsoft-json-linq-jtoken-is-a-rec

So, no go. I now have a choice between switching to NewtonSoft serializer or explicitly defining the content type. Switching to NewtonSoft is desirable, but risky for a big web service already in production. Creating a class for Address is no big deal, but it means that on every read I will be deserializing JSON from the internal storage, only to have it serialized again by WCF. Looks like a lot of unnecessary monkey business and possible performance hit (I know, it’s premature optimization, but still).

Leave a Reply

Your email address will not be published. Required fields are marked *