Fason 0.8.0

dotnet add package Fason --version 0.8.0
                    
NuGet\Install-Package Fason -Version 0.8.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Fason" Version="0.8.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Fason" Version="0.8.0" />
                    
Directory.Packages.props
<PackageReference Include="Fason" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Fason --version 0.8.0
                    
#r "nuget: Fason, 0.8.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Fason@0.8.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Fason&version=0.8.0
                    
Install as a Cake Addin
#tool nuget:?package=Fason&version=0.8.0
                    
Install as a Cake Tool

Fason

An F# code generator that generates optimized (and Thoth-compatible) JSON encoders and decoders for F# types, without using reflection. Supports F# types (records, unions, tuples, collections, etc.) and .NET primitives and core types. See Supported types below for the full list. Compatible with Fable.

Usage

First, add Fason as a dependency to your project via NuGet. You need to also install the Fason code generation tool by running dotnet tool install Fason.Tool.

Then, annotate any types you want JSON encoders / decoders to be generated for with the FasonSerializable attribute. For example:

open Fason

[<FasonSerializable>]
type MyRecord = {
    hello: string
    world: int
}

To generate the encoders / decoders, run dotnet fason MyProject.fsproj, where MyProject.fsproj is the path to your F# project. This will create a Fason.Generated.fs file next to your project file. You can pass several projects at once by separating them with spaces.

Include the generated file in your project and call the generated Codecs.Register() once at startup, before any JSON conversion. Then use Json.serialize, Json.deserialize<'T>, Json.serializeObj, and Json.deserializeObj from the Fason namespace to convert your values between JSON and back. The Obj-suffixed functions take a Type and an obj instead of a concrete object, for cases where it might not be known at runtime.

open Fason

MyProject.Serialization.Codecs.Register()

let json = Json.serialize { hello = "hi"; world = 1 }
let value = Json.deserialize<MyRecord> json
let jsonObj = Json.serializeObj typeof<MyRecord> (box value)

Attributes

Fason looks for three attributes, all in the Fason namespace:

Attribute Description
FasonSerializable Marks a type to generate encoders / decoders for, together with every type it depends on. On a module, it applies to everything inside. On an interface, codecs are generated for the parameter and return types of its members.
FasonUnwrap Generates encoders / decoders for the type arguments of a type instead of itself. Task<'T>, ValueTask<'T> and Async<'T> are treated this way by default.
FasonIgnore Marks a type that is skipped (no encoders / decoders are generated for it). Types that depend on it get none either.

For example, this makes Fason generate codecs for Request, Response and ServerStatus, but not for Session, Audit or Deferred<'T>:

namespace MyNamespace

open System.Threading.Tasks
open Fason

type ServerStatus = { ok: bool }

[<FasonSerializable>]
module Api =
    type Request = { id: int }
    type Response = { ok: bool }

    [<FasonIgnore>]
    type Session = { token: string }

    type Audit = { session: Session; at: System.DateTime }

    [<FasonUnwrap>]
    type Deferred<'T>(compute: unit -> 'T) =
        member _.Value = compute ()

    type IApi =
        abstract member Send: request: Request -> Task<Response>
        abstract member Status: unit -> Deferred<ServerStatus>

Supported types

Fason supports generating encoders / decoders for these types, and for any combination of them:

Kind Types JSON
Numbers int8, int16, int32, int64, uint8, uint16, uint32, uint64, single, double, decimal A number. int64, uint64 and decimal are quoted, so JavaScript doesn't lose precision.
Other primitives bool, char, string, unit unit is null.
Core types Guid, DateTime, DateTimeOffset, DateOnly, TimeOnly, TimeSpan Encoded as strings. Dates and times in ISO 8601, TimeSpan its default format.
Records Records, anonymous records, generic records. Fields that are None or ValueNone are omitted.
Unions Discriminated unions, generic unions, Result<'T, 'TError> The case name as a string, or an array of the case name string followed by its fields.
Enums Enums over any of the supported integer types The underlying number. Reading also accepts the name of the value as a string.
Tuples Tuples of any arity Encoded as an array.
Collections 'T array, 'T list, 'T seq, Set<'T>, Map<'K, 'V> Encoded as an array. Maps with string or Guid keys are objects, other maps are arrays of pair arrays.
Options 'T option, 'T voption The value, or null.
Units of measure Any supported type with a unit of measure, for example float<kg> or string<userId>. For string UoMs Fason expects FSharp.UMX to be available. The same as the type without the UoM.
Interfaces Interfaces marked FasonSerializable (or anywhere down the chain of something marked as such). No codecs for the interface itself, only for the argument and return types of its member functions.

Options

The Fason tool has the following options:

dotnet fason MyProject.fsproj... [--namespace <name>] [--output <path>] [--configuration <name>] [--hermes] [--watch] [--version]
Option Description
--namespace <name> Allows specifying the namespace the generated F# code will be put under. Single project only.
--output <path> Specify the path the generated code will be placed under. Can be a directory or a full path with a filename. Single project only.
--configuration <name> Build configuration the project is loaded with (default: Debug). Referenced projects must already be built in it.
--hermes Optimize the generated code for usage with the Hermes JS engine (probably also helps with other JS interpreters).
--watch Keep running and regenerate whenever a source file of a loaded project changes.
--version Print the tool version and exit.

Instead of --namespace and --output, a project can set the FasonNamespace and FasonOutput MSBuild properties. A relative FasonOutput is resolved against the project directory. This is the way to configure several projects in one run.

Benchmarks

Below are some benchmarks compared to Thoth's auto coders on different runtimes, averaged across 5 runs with many iterations. Those are against several documents of varying complexities and sizes (most are from sensitive production data and are not included in this repo). Keep in mind that this is just runtime encoding / decoding performance. Since this tool generates code, you're basically trading build time for this. In a real-world project I've been testing this on, the generated file added ~2s to the build time. Generating the code itself took an additional ~10s.

.NET 10

Document Serialize Thoth Deserialize Thoth
84 B 0.22 µs 15 µs 0.36 µs 15 µs
10 KB 8.8 µs 2.8 ms 14 µs 8.0 ms
100 KB 72 µs 23 ms 151 µs 67 ms
633 KB 0.85 ms 127 ms 1.08 ms 318 ms
5.7 MB 8.0 ms 1203 ms 15.7 ms 2355 ms

Fable + Node 22

Document Serialize Thoth Deserialize Thoth
84 B 0.50 µs 1.10 µs 0.50 µs 2.04 µs
10 KB 25 µs 180 µs 30 µs 539 µs
100 KB 475 µs 1.30 ms 403 µs 3.55 ms
633 KB 1.18 ms 5.19 ms 2.04 ms 12.8 ms
5.7 MB 36.7 ms 75.2 ms 32.9 ms 176 ms

Fable + Hermes

Document Serialize Thoth Deserialize Thoth
84 B 1.12 µs 5.18 µs 0.72 µs 8.18 µs
10 KB 67 µs 1.06 ms 90 µs 2.09 ms
100 KB 744 µs 5.08 ms 957 µs 16.0 ms
633 KB 3.18 ms 28.2 ms 6.92 ms 66.2 ms
5.7 MB 50.1 ms 257 ms 69.8 ms 829 ms
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0 206 9/7/2026
0.7.0 84 9/7/2026
0.6.0 94 9/5/2026
0.5.0 91 9/5/2026
0.4.0 90 9/4/2026
0.3.0 100 9/4/2026
0.2.0 92 9/4/2026
0.1.0 88 9/3/2026