Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use F# Union types with Servicestack JSON serialization?

I guess it is too much I am asking from a framework. But just wondering if it is possible at all. Or what will be work around for this.

JSON.Net with new release started supporting F# union types. What is the work around for that, means if I am using servicestack.text, then how can I flatten union types to support serialization.

Here is code sample for both.

type Shape =
    | Rectangle of width : float * length : float
    | Circle of radius : float
    | Empty


[<EntryPoint>]
let main argv = 
//    printfn "%A" argv
    let shape1 = Rectangle(1.3, 10.0)

    let json = JsonConvert.SerializeObject(shape1) //JSON.net
    printfn "%A" json
    // {
    //   "Case": "Rectangle",
    //   "Fields": [
    //     1.3,
    //     10.0
    //   ]
    // }

    let shape2 = JsonConvert.DeserializeObject<Shape>(json) //JSON.Net
    printfn "%A" (shape2 = shape1) //true

    let sJson = JsonSerializer.SerializeToString shape1 //SS.Text
    printfn "%A" sJson

    let sShape2 = JsonSerializer.DeserializeFromString sJson //SS.Text
    printfn "%A" (sShape2 = shape1) //false

    Console.Read() |> ignore
    0 // return an integer exit code

Deserialization using servicestack is returning false. And also generated json string is quite complicated in compare to JSON.net.

How to achieve proper serialization for Union types?

like image 305
kunjee Avatar asked Mar 13 '14 05:03

kunjee


1 Answers

You'll have to give ServiceStack a custom serializer. It would like this, using a smaller Shape type for brevity:

open ServiceStack.Text

type Shape =
    | Circle of float
    | Empty

JsConfig<Shape>.SerializeFn 
    <- Func<_,_> (function
                  | Circle r -> sprintf "C %f" r
                  | Empty -> sprintf "E")

JsConfig<Shape>.DeSerializeFn 
    <- Func<_,_> (fun s ->    
                    match s.Split [| |] with
                    | [| "C"; r |] -> Circle (float r)
                    | [| "E" |] -> Empty)

let shapes = [| Circle 8.0 |]
let json = JsonSerializer.SerializeToString(shapes)
let shapes1 = JsonSerializer.DeserializeFromString<Shape[]>(json)
let is_ok = shapes = shapes1

This code does not have proper exception-propagation in the deserializer: you'd want to handle the match not matching, and float might raise a System.FormatException.

like image 89
Søren Debois Avatar answered Oct 01 '22 17:10

Søren Debois