Skip to content

F# Guide — JSON: Parsing and Generating JSON Data

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about F# Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

F# JSON processing leverages type providers for compile-time types and libraries like System.Text.Json for runtime serialization, enabling type-safe JSON handling with minimal code.

What You'll Learn

  • JSON type provider for type-safe access
  • System.Text.Json serialization
  • FSharp.Json library
  • Manual JSON construction
  • Performance considerations

Why It Matters

JSON is the universal data exchange format for APIs, configuration, and data storage. Durga Antivirus Pro uses JSON for API responses and configuration files.

Real-World Use

REST API clients, configuration files, data export/import, and web service communication.

flowchart LR
    A["JSON"] --> B["Type Provider"]
    B --> C["System.Text.Json"]
    C --> D["Manual"]
    D --> E["Performance"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

JSON Type Provider

open FSharp.Data

// Define from sample
type User = JsonProvider<"""
  {"name":"Alice","email":"alice@example.com","age":30}
""">

// Parse JSON
let json = """{"name":"Bob","email":"bob@example.com","age":25}"""
let user = User.Parse(json)

user.Name   // "Bob"
user.Email  // "bob@example.com"
user.Age    // 25

Arrays with Type Provider

open FSharp.Data

type Users = JsonProvider<"""
  [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
""">

let json = """[{"id":3,"name":"Carol"},{"id":4,"name":"Dave"}]"""
let users = Users.Parse(json)

for user in users do
    printfn "%d: %s" user.Id user.Name

System.Text.Json

open System.Text.Json
open System.Text.Json.Serialization

// Define record for serialization
[<CLIMutable>]
type Person = {
    Name: string
    Email: string
    Age: int
}

// Serialize
let person = { Name = "Alice"; Email = "a@example.com"; Age = 30 }
let json = JsonSerializer.Serialize(person)
// {"Name":"Alice","Email":"a@example.com","Age":30}

// Deserialize
let deserialized: Person = JsonSerializer.Deserialize(json)

Custom Serialization

open System.Text.Json

// Custom converter for discriminated unions
type Shape = Circle of float | Rectangle of float * float

let options = JsonSerializerOptions()
options.Converters.Add(FSharp.SystemTextJson.UnionConverter())

let json = JsonSerializer.Serialize(Circle 5.0, options)
let shape = JsonSerializer.Deserialize<Shape>(json, options)

Manual JSON Construction

open System.Text.Json

// Build JSON manually
let buildJson name email age =
    use doc = JsonDocument.Parse(
        sprintf """{"name":"%s","email":"%s","age":%d}""" name email age)
    doc.RootElement

// Using JsonObject (System.Text.Json.Nodes)
open System.Text.Json.Nodes

let node = JsonObject()
node["name"] = "Alice"
node["age"] = 30
let json = node.ToJsonString()

Working with Nested JSON

type Address = {
    Street: string
    City: string
    Zip: string
}

type Employee = {
    Name: string
    Address: Address
}

// Nested JSON
let json = """{"Name":"Alice","Address":{"Street":"123 Main","City":"Portland","Zip":"97201"}}"""
let employee = JsonSerializer.Deserialize<Employee>(json)

Performance

// System.Text.Json is the fastest option
// JsonProvider is convenient but slower
// For high throughput, use Utf8JsonReader/Writer

// Manual Utf8JsonReader for streaming
let readJsonStream (stream: System.IO.Stream) =
    use reader = new System.Text.Json.Utf8JsonReader(
        new System.Buffers.ArrayBufferWriter<byte>())
    // Process tokens manually for maximum speed

Common Mistakes

1. Case sensitivity

JSON is case-sensitive. Ensure property names match between JSON and F# types.

2. Null handling

JSON null values map to F# options. Use string option or Option.ofObj for nullable fields.

3. Missing properties

Use JsonProvider with optional fields via JsonProvider<"...", SampleIsList=true> to handle missing fields.

4. Large JSON documents

Use streaming (Utf8JsonReader) for large documents instead of Parsing the entire string.

5. Type provider cache

Type providers cache schema. Clean and rebuild if the sample data changes.

Practice Questions

1. What are two ways to handle JSON in F#? JsonProvider for compile-time types and System.Text.Json for runtime serialization.

2. How do you handle optional JSON fields? Use F# option types for nullable fields and JsonProvider with proper sample configuration.

3. Which JSON library is fastest? System.Text.Json is the fastest in the .NET ecosystem, being hardware-accelerated and allocation-efficient.

Challenge: Build a JSON configuration reader that deserializes a nested configuration structure.

FAQ

{{< faq question="Can JsonProvider handle dynamic JSON?" >} JsonProvider requires a sample. For fully dynamic JSON, use JsonDocument.Parse directly. {{< /faq >}}

{{< faq question="Is there a JSON computation expression?" >} Not built-in, but the FSharp.SystemTextJson library provides good interop with discriminated unions and records. {{< /faq >}}

{{< faq question="How do I handle camelCase JSON?" >} Use JsonSerializerOptions(PropertyNamingPolicy = JsonNamingPolicy.CamelCase) with System.Text.Json. {{< /faq >}}

{{< faq question="Can JSON be pretty-printed?" >} Yes: JsonSerializer.Serialize(value, new JsonSerializerOptions(WriteIndented = true)). {{< /faq >}}

{{< faq question="What is JsonDocument?" >} A lightweight read-only JSON parser that provides a DOM-like access pattern without deserializing to specific types. {{< /faq >}}

Mini Project

Build a JSON-based configuration system:

type Config = {
    Host: string
    Port: int
    Features: string list
    Database: {| Provider: string; ConnectionString: string |}
}

let loadConfig path =
    let json = System.IO.File.ReadAllText(path)
    JsonSerializer.Deserialize<Config>(json)

// config.json:
// {"Host":"localhost","Port":8080,"Features":["auth","logging"],"Database":{"Provider":"Postgres","ConnectionString":"host=localhost db=app"}}

let config = loadConfig "config.json"
printfn "Starting on %s:%d" config.Host config.Port

What's Next

Now that you understand JSON processing, explore HTTP clients for web API communication.

Topic Description Link
F# HTTP HTTP clients {{< ref "25-http" >}}
F# Testing Unit Testing {{< ref "26-testing" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro