14

Json.NET 6.0.1 adds F# support for records and discriminated unions. When serializing a F# record type using Json.NET I now get nicely formatted JSON.

The serialization is done as follow:

let converters = [| (new StringEnumConverter() :> JsonConverter) |]
JsonConvert.SerializeObject(questionSet, Formatting.Indented, converters)

However, when I try to expose my F# types through a ASP.NET WebApi 5.0 service, written in C#, the serialized JSON includes an @-sign infront of all properties. The @-sign comes from the internal backing field for the record type (this used to be a known problem with Json.Net and F#).

But - since I'm using the updated version of Json.NET, shouldn't the result be the same as when calling JsonConvert? Or is JsonConvert behaving differently than JsonTextWriterand JsonTextReader?

As far as I can tell from reading the JsonMediaTypeFormatter in the WebApi source JsonTextWriterand JsonTextReader is used by WebApi.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Jonas Follesø
  • 6,441
  • 7
  • 41
  • 54

2 Answers2

9

You can adorn your records with the [<CLIMutable>] attribute:

[<CLIMutable>]
type MyDtr = {
    Message : string
    Time : string }

That's what I do.


For nice XML formatting, you can use:

GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer <- true

For nice JSON formatting, you can use:

config.Formatters.JsonFormatter.SerializerSettings.ContractResolver <-
    Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    Well, I guess it would work, but it doesn't really answer the question (why the serialized JSON comming out of WebAPI is different from calling JsonConvert directly). Now that Json.NET supports F# properly we shouldn't have to make customizations like that... – Jonas Follesø Apr 02 '14 at 08:09
  • 2
    I know, and I haven't dived into the reason for that... I don't claim that I answer *that* question, but I thought that, until you do get an answer to that question, I'd share what is known to *work*, at least :) – Mark Seemann Apr 02 '14 at 08:15
  • Yeah, agreed - and thanks alot - will go for this solution for now, as I don't have time right now to dig deeper into the Json.NET and/or WebAPI source code. Cheers, Jonas – Jonas Follesø Apr 02 '14 at 08:43
  • I have a record decorated by the attribute but `DefaultContractResolver` still appends `@`. The only way to make it working was this: http://stackoverflow.com/questions/13037472/serializing-f-record-type-to-json-includes-character-after-each-property – abatishchev Dec 21 '14 at 22:29
  • @abatishchev Have you followed the link and read the article? It explains that you also have to modify the Formatter. – Mark Seemann Dec 22 '14 at 17:21
  • So only XML formatter is 'compatible' with F#, not Json one. For instance, I have XML formatter completely removed, client are JSON only. – abatishchev Dec 23 '14 at 22:27
  • Mark can you make it clearer in your answer that this is an either/or scenario. I think adding CLIMutable just to control the serialized representation is a *bad* thing - prefer @Isaac's answer http://stackoverflow.com/a/26036775/26167 – piers7 Jan 23 '17 at 07:21
7

I believe it's because the backing fields that are emitted by F# records don't follow the same naming convention as C# property backing fields.

The easiest way I've found to get around this is to change the ContractResolver at the startup of your web application from the System.Net.Http.Formatting.JsonContractResolver to use the Newtonsoft.Json.Serialization.DefaultContractResolver instead: -

Formatters.JsonFormatter.SerializerSettings.ContractResolver <- DefaultContractResolver()

You'll then get all JSON formatting done via Newtonsoft's JSON formatter rather than the NET one.

Isaac Abraham
  • 3,422
  • 2
  • 23
  • 26
  • +1. By default Web API makes Newtonsoft use System.Web.Http.Formatting.JsonContractResolver, which explains why 'vanilla' Json.net serialization from code behaves differently to web api. Setting back to NewtonSoft defaults works (without forcing to camelCase, which you may or may not work) – piers7 Jan 23 '17 at 07:15
  • Your post answered my question here:https://stackoverflow.com/questions/58209838/f-record-with-option-field-doesnt-deserialize-properly-in-asp-net-webapi-2-x-a/58224359#58224359. However, I still have to get my head around the differences between the DefaultContractResolver and the one that is used by default. I found here the source code: https://github.com/aspnet/AspNetWebStack/blob/v3.2-rtm/src/System.Net.Http.Formatting/Formatting/JsonContractResolver.cs – boggy Oct 03 '19 at 17:48