Steven
Steven

Reputation: 3292

Generic function to read from a `.json` file

So, I can easily write an arbitrary type to JSON with Newtonsoft.Json:

type X = {
Number: decimal
Sequence: decimal
NumList: decimal list
}

let createItem (n, s, nL) = 
    {Number = n; 
    Sequence = s; 
    NumList = nL}

let items =
    [
        (1M, 1M, [1M; 2M; 3M])
        (2M, 2M, [2M; 4M; 6M])
        (3M, 3M, [3M; 6M; 9M])
    ]
    |> List.map createItem

open Newtonsoft.Json
open System.IO

let writeToJson (path: string) (obj: 'a) : unit =
    let serialized = JsonConvert.SerializeObject(obj)

    File.WriteAllText(path, serialized)

writeToJson "xList.json" items    

How can I write a function generic enough that I can read a JSON file? In other words, I'd like something like:

let readFromJson (path: string) (t: 'T) = 
    let convertToQr = File.ReadAllText(path)

    Newtonsoft.Json.JsonConvert.DeserializeObject<t list>(convertToQr)

where the second argument is the Type of the object in path, but I don't know how to do that. If I try to use this function as is, I get a compiler error.

How can I declare in the second argument above the type of the thing that is in path? Can I?

Upvotes: 1

Views: 380

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80744

Generic parameters, when explicitly defined, are written in angle brackets immediately after function name, before regular parameters:

let readFromJson<'T>(path: string) =
   let convertToQr = File.ReadAllText(path) 

   Newtonsoft.Json.JsonConvert.DeserializeObject<'T list>(convertToQr)

Usage:

readFromJson<string> "/some/file.json"

Alternatively, you can specify the return type of your function, and let the compiler infer all generic parameters and arguments for you:

let readFromJson(path: string) : 't list =
   let convertToQr = File.ReadAllText(path) 

   Newtonsoft.Json.JsonConvert.DeserializeObject(convertToQr)

Here, the compiler knows that the generic argument of DeserializeObject must be 't list, because its result is being returned from readFromJson, and the result type of readFromJson is explicitly declared to be 't list. Similarly, just by noticing a generic type in the function definition, the compiler will infer that the function has one generic parameter.

In a similar way, you can let the compiler infer the required type when you call the function:

// call inferred to readFromJson<string>, because that's the required return type
let s: string list = readFromJson "/some/file.json"   

Upvotes: 6

Related Questions