Json in F#
open System
open System.Text.Json
open System.Text.Json.Serialization
// We'll use these two record types to demonstrate encoding and
// decoding of custom types below.
type Response1 = {
Page: int
Fruits: string[]
}
// Only fields with public setters will be encoded/decoded in JSON.
// We can use attributes to customize the encoded JSON key names.
type Response2 = {
[<JsonPropertyName("page")>]
Page: int
[<JsonPropertyName("fruits")>]
Fruits: string[]
}
let main() =
// First we'll look at encoding basic data types to
// JSON strings. Here are some examples for atomic values.
let bolB = JsonSerializer.Serialize(true)
printfn "%s" bolB
let intB = JsonSerializer.Serialize(1)
printfn "%s" intB
let fltB = JsonSerializer.Serialize(2.34)
printfn "%s" fltB
let strB = JsonSerializer.Serialize("gopher")
printfn "%s" strB
// And here are some for arrays and maps, which encode
// to JSON arrays and objects as you'd expect.
let slcD = [|"apple"; "peach"; "pear"|]
let slcB = JsonSerializer.Serialize(slcD)
printfn "%s" slcB
let mapD = Map.ofList [("apple", 5); ("lettuce", 7)]
let mapB = JsonSerializer.Serialize(mapD)
printfn "%s" mapB
// The JSON package can automatically encode your
// custom data types.
let res1D = { Page = 1; Fruits = [|"apple"; "peach"; "pear"|] }
let res1B = JsonSerializer.Serialize(res1D)
printfn "%s" res1B
// You can use attributes on record field declarations
// to customize the encoded JSON key names. Check the
// definition of `Response2` above to see an example
// of such attributes.
let res2D = { Page = 1; Fruits = [|"apple"; "peach"; "pear"|] }
let res2B = JsonSerializer.Serialize(res2D)
printfn "%s" res2B
// Now let's look at decoding JSON data into F#
// values. Here's an example for a generic data
// structure.
let byt = """{"num":6.13,"strs":["a","b"]}"""
// We need to provide a type where the JSON
// package can put the decoded data. This
// Map<string, JsonElement> will hold a map of strings
// to arbitrary data types.
let dat = JsonSerializer.Deserialize<Map<string, JsonElement>>(byt)
printfn "%A" dat
// In order to use the values in the decoded map,
// we'll need to convert them to their appropriate type.
// For example here we convert the value in `num` to
// the expected `float` type.
let num = (dat.["num"].GetDouble())
printfn "%f" num
// Accessing nested data requires a series of
// conversions.
let strs = dat.["strs"].EnumerateArray() |> Seq.map (fun x -> x.GetString()) |> Seq.toArray
let str1 = strs.[0]
printfn "%s" str1
// We can also decode JSON into custom data types.
// This has the advantages of adding additional
// type-safety to our programs and eliminating the
// need for type assertions when accessing the decoded
// data.
let str = """{"page": 1, "fruits": ["apple", "peach"]}"""
let res = JsonSerializer.Deserialize<Response2>(str)
printfn "%A" res
printfn "%s" res.Fruits.[0]
// In the examples above we always used strings as
// intermediates between the data and JSON representation.
// We can also stream JSON encodings directly to TextWriters
// like Console.Out or even HTTP response streams.
let d = Map.ofList [("apple", 5); ("lettuce", 7)]
JsonSerializer.Serialize(Console.Out, d)
main()
This F# code demonstrates JSON encoding and decoding using the System.Text.Json
namespace, which is the modern JSON handling library in .NET. Here are some key points about the translation:
F# uses
record
types instead ofstruct
types for defining custom data structures.The
json
tags in Go are replaced withJsonPropertyName
attributes in F#.F# uses
Map<'Key, 'Value>
instead of Go’smap[KeyType]ValueType
.The
interface{}
type in Go is roughly equivalent toJsonElement
in F#’s JSON handling.F# uses pattern matching and functional programming constructs, which can make some operations more concise.
Error handling in F# is typically done through the use of
Option
types or exceptions, rather than explicit error returns as in Go.F# doesn’t have a direct equivalent to Go’s
panic
, but you can use exceptions for similar behavior.The
JsonSerializer
class inSystem.Text.Json
provides methods for both serialization and deserialization.
This code provides a comprehensive overview of JSON handling in F#, covering encoding of basic types, custom types, decoding into generic and specific types, and working with nested JSON structures.