The Web

Microsoft’s WCF DataContractJsonSerializer sucks!

Anybody who has tried to write a RESTful JSON service in WCF will have hit a wall of hurt as soon as they try to do anything beyond returning a simple string. Enums don’t work how you would want (returning a string instead of the integer value), and error messages are still returned as either XML or (even worse) HTML.

There are some partial solutions online, particularly a great starting point provided by Jeroen Hildering on his blog post, however I found there were still a few further issues I wanted to solve.

In the interests of saving people any future pain, I’ve decided to put my finished code up on GitHub.

It is very simple to implement, firstly you have to add the Newtonsoft.Json library to your project – I’ll leave this as an exercise! After that just drop the file into your WCF project, and add the following lines to your Web.config:

In the system.serviceModel/behaviors/endpointBehaviors/behavior node, add the following below your webHttp node:

    <newtonsoftJsonBehavior includeExceptionDetailInFaults="true" />

In the system.serviceModel/bindings/webHttpBinding/binding node, add the following attribute:

    contentTypeMapper="JonGrant.Json.NewtonsoftJsonContentTypeMapper, YourAssemblyNameHere"

Add the following XML inside the system.serviceModel node:

    <extensions>
      <behaviorExtensions>
        <add name="newtonsoftJsonBehavior" type="JonGrant.Json.NewtonsoftJsonBehaviorExtension, YourAssemblyNameHere" />
      </behaviorExtensions>
    </extensions>

You can also see a sample Web.config file also on GitHub, if that helps.

9 comments

  • JCallico

    Thanks a lot for sharing your solution.

    After applying your changes I noticed that the responses contained some unexpected characters at the beginning of the body, for example:

    {"DateOfBirth": "1982-03-09T00:00:00"}

    I modified the FormatObjectAsMessage method as follows and the unexpected characters were not longer there:

    public static Message FormatObjectAsMessage(object obj, MessageVersion messageVersion, string action, HttpStatusCode statusCode)
    {
    //byte[] body;
    //var serializer = new JsonSerializer();
    //serializer.Converters.Add(new StringEnumConverter { AllowIntegerValues = false, CamelCaseText = false });
    //
    //using (var ms = new MemoryStream())
    //{
    // using (var sw = new StreamWriter(ms, Encoding.UTF8))
    // {
    // using (JsonWriter writer = new JsonTextWriter(sw))
    // {
    // writer.Formatting = Newtonsoft.Json.Formatting.Indented;
    // serializer.Serialize(writer, obj);
    // sw.Flush();
    // body = ms.ToArray();
    // }
    // }
    //}

    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false, CamelCaseText = false });
    string jsonString = JsonConvert.SerializeObject(obj, settings);
    byte[] body = Encoding.ASCII.GetBytes(jsonString);

    Message replyMessage = Message.CreateMessage(messageVersion, action, new RawBodyWriter(body));
    replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
    var respProp = new HttpResponseMessageProperty();
    respProp.Headers[HttpResponseHeader.ContentType] = "application/json";
    respProp.StatusCode = statusCode;
    replyMessage.Properties.Add(HttpResponseMessageProperty.Name, respProp);
    return replyMessage;
    }

    Can you explain this behavior?

    • Jon

      Yes. The characters you are seeing are the UTF8 byte-order marks. If your target system can’t handle these, it can’t parse UTF8.

      You can change it to ASCII by changing the line:

      using (var sw = new StreamWriter(ms, Encoding.UTF8))

      to be:

      using (var sw = new StreamWriter(ms, Encoding.ASCII))

  • JCallico

    The system handling the response support UTF8, as a matter of fact I changed the code to use UFT8.GetBytes and it also works.

    public static Message FormatObjectAsMessage(object obj, MessageVersion messageVersion, string action, HttpStatusCode statusCode)
    {
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false, CamelCaseText = false });
    byte[] body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj, settings));

    Message replyMessage = Message.CreateMessage(messageVersion, action, new RawBodyWriter(body));
    replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
    var respProp = new HttpResponseMessageProperty();
    respProp.Headers[HttpResponseHeader.ContentType] = "application/json";
    respProp.StatusCode = statusCode;
    replyMessage.Properties.Add(HttpResponseMessageProperty.Name, respProp);
    return replyMessage;
    }

  • JCallico

    Thanks.

    Do you see any danger in keeping my changes and not using the UTF8 “preamble”?

  • JCallico

    I’m not sure how many people are currently using Json endpoints in WCF but I have to say that I spent a decent amount of time looking for a solution to replace the default serializer and your code was the easier to use.

    It will be great if your code was available as a nuget package.

  • Richard

    Thanks Jon. Your code saved me a lot of work. Agree, this would make a good NuGet package.

Leave a Reply