Hi,

last time I decided to rewrite one of the services from C# to F#. One of the problems was to keep the integrity in terms of contract generated via this project. Previously the contract contains an enum field which I decided to represent as a union type in F#. By default Newtonsoft.Json which we are using to serialize data, serialize differently enum from C# and union type from F#. So going to the details of a problem. The contract looked like this:

public enum SomeEnum 
{ 
    Value1,
    Value2
}

public class Contract 
{ 
    public string Name { get; set; }
    public SomeEnum Type { get; set; }
}

The serialized contract, which were consumed by a front-end app looked like this:

{ 
    Type: value1,
    Name: name,
}

Rewriting an object to F# wasn’t a hard task. As I said before I decided that enum would be represented as a union type and a class as a Type. So the code in F# looked like this:

type SomeEnum = Value1 | Value2 

type Contract = 
    { 
        Name: string 
        Type: SomeEnum 
    } 

The problem appears during the serialization of this type and passing it to a front-end. Because the serialized object looked like this:

{ 
    Type: { Case: value1 } 
    Name: name 
} 

So as we could see this json looks a little bit different than the previous one. Because of that I decided to write my own converter for union types. The first thing was a serialization. What I want to achieve in the serialized object is information only about the value of union type as a string. But this value has to start with a lower case. Keeping in mind what I plan about that here is a piece of code to do that:

type EnumAsStringConverter () = 
    inherit JsonConverter() 
    
    override this.CanConvert objectType = true; 
    override this.WriteJson (writer: JsonWriter, value: obj, serializer: JsonSerializer): unit = 
        if value = null then writer.WriteValue value 
        else 
            let str = value.ToString() 
            let firstLetter = Char.ToLowerInvariant(str.[0]) 
            let theRest = str.Substring(1) 
            writer.WriteValue(sprintf "%c%s" firstLetter theRest) 

This code has a task to get the actual union value and then invoke a ‘toString’ on it. But keeping in mind that it has to start with a lower case. So I run my tests which checks a compatibility with the old json and everything seems to work fine. But here appeared a problem. What with deserialization to a union type? Right now there is no possibility to deserialize string to union. To do that I override a read method which looks right now like this:

...
override this.CanRead = true 

override this.ReadJson (reader: JsonReader, objectType: Type, existingValue: obj, serializer: JsonSerializer) : obj = 
    if reader.TokenType <> JsonToken.String then 
        (sprintf "Cannot deserialize %A type to Union" reader.TokenType) |> ArgumentException |> raise 
    else 
        if FSharpType.IsUnion (objectType, BindingFlags.Public) then 
            let matchedCase = FSharpType.GetUnionCases(objectType, BindingFlags.Public) |> Array.tryFind (fun x -> x.Name.ToLower() = (reader.Value :?> string).ToLower()) 
            match matchedCase with 
            | Some s -> FSharpValue.MakeUnion(s, [||]) 
            | None -> (sprintf "Cannot find maching case %A in union case: %A" reader.Value objectType) |> ArgumentException |> raise 
        else (sprintf "objectType is not an union, instead it is a: %A" objectType) |> ArgumentException |> raise 

To create an ACL at first I checked if the value which I want to deserialize is a string and if the type to which I want to deserialize is a union type. Then I looked in all of the fields of a union and check if it has a value like this ignoring the case. If I found one I create a union with the following value. I run tests one more time and everything is green.

To sum up moving parts of C# code to F# with keeping the backward compatibility in a contract could be problematic. And sometimes we have to do some extra work to achieve this. But still, I think that because of easy writing programs in F# in comparison to C# sometimes it is worth to take a while to look and do this.

Thanks for reading!