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.

13 Replies to “Microsoft’s WCF DataContractJsonSerializer sucks!”

  1. 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?

    1. 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))

  2. 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;
    }

    1. This is because the StreamWriter inserts the byte order marks, while Encoding.UTF8.GetBytes does not – when using this method to get the preamble there you are supposed to call Encoding.UTF8.GetPreamble() and prepend this.

      See the answers here:

      http://stackoverflow.com/questions/5266069/streamwriter-and-utf-8-byte-order-marks
      http://stackoverflow.com/questions/420867/why-isnt-the-byte-order-mark-emitted-from-utf8encoding-getbytes

      Whatever you are consuming the service with doesn’t know how to handle the (albeit optional) byte order marks of UTF8.

  3. 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.

  4. Dear friend, thanks a lot for sharing this, I was struggling on the web all day. I think you deserve a Nobel prize for this!

  5. This is great, thank you.

    But there’s one problem. As soon as I attempt to change the response status, for example:

    WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Created;

    I suddenly get content-type application/xml response instead of application/json.

    The response itself is completely fine, it’s JSON. But because of content-type, some clients get tripped by this issue. Firefox also shows an error about malformed XML (of course – it’s JSON and not XML).

    The debugger clearly shows that respProp.Headers[HttpResponseHeader.ContentType] = “application/json”; is being set. However, WCF somehow manages to override it to its own content-type. There is no such a problem with the default datacontractserializer.

    What’s even stranger – not only setter of WebOperationContext.Current.OutgoingResponse.StatusCode triggers this behavior but also getter:

    var sc = WebOperationContext.Current.OutgoingResponse.StatusCode;

    The only workaround I found was to add a line:

    WebOperationContext.Current.OutgoingResponse.ContentType = “application/json”;
    right under the line
    respProp.Headers[HttpResponseHeader.ContentType] = “application/json”;

    And just to be safe, I also changed

    return FormatObjectAsMessage(result, messageVersion, operation.Messages[1].Action, HttpStatusCode.OK);

    to

    return FormatObjectAsMessage(result, messageVersion, operation.Messages[1].Action,
    WebOperationContext.Current.OutgoingResponse.StatusCode);

Leave a Reply

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