Header menu logo Fabulous.AST

Build-Time Type Generation

Automatically generate F# types from JSON files at build time using Fabulous.AST.Build.

This extension provides an MSBuild task that watches JSON files and generates F# record types during compilation.

Installation

dotnet add package Fabulous.AST.Build

💡 Tip: For programmatic control over generation, see the JSON Extension tutorial.

Quick Start

1. Add a JSON schema file

Create schemas/user.json:

{
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "isActive": true
}

2. Configure your project

Add to your .fsproj:

<ItemGroup>
  <FabulousAstJson Include="schemas/user.json" />
</ItemGroup>

Note: Generated files are automatically included in compilation. No manual <Compile Include="..."> is needed.

3. Build

dotnet build

This generates Generated/user.Generated.fs:

type Root = {
    id: int
    name: string
    email: string
    isActive: bool
}

Configuration Options

Custom Root Type Name

Override the default Root type name:

<FabulousAstJson Include="schemas/user.json" RootName="User" />

Add a Module

Wrap types in a file-level module:

<FabulousAstJson Include="schemas/user.json"
                 RootName="User"
                 ModuleName="MyApp.Models" />

Generates:

module MyApp.Models

type User = { id: int; name: string; email: string; isActive: bool }

Note: Each file must have a unique module name since file-level modules cannot share the same fully-qualified name.

Custom Output Directory

Change the output location:

<PropertyGroup>
  <FabulousAstOutputFolder>Types</FabulousAstOutputFolder>
</PropertyGroup>

Custom Output Filename

Override the default {name}.g.fs pattern:

<FabulousAstJson Include="schemas/user.json" OutputFileName="UserTypes.fs" />

Multiple Files

Individual Configuration

<ItemGroup>
  <FabulousAstJson Include="schemas/user.json"
                   RootName="User"
                   ModuleName="MyApp.Models.User" />
  <FabulousAstJson Include="schemas/product.json"
                   RootName="Product"
                   ModuleName="MyApp.Models.Product" />
</ItemGroup>

Glob Patterns

Process all JSON files in a directory:

<ItemGroup>
  <FabulousAstJson Include="schemas/**/*.json" />
</ItemGroup>

Type Inference Examples

Nested Objects

Input (company.json):

{
    "name": "Acme Corp",
    "address": {
        "street": "123 Main St",
        "city": "London"
    }
}

Output:

type Address = { street: string; city: string }
type Company = { name: string; address: Address }

Arrays

Input (users.json):

[
    { "id": 1, "name": "Alice" },
    { "id": 2, "name": "Bob" }
]

Output:

type UsersItem = { id: int; name: string }
type Users = UsersItem list

Optional Fields

Fields missing in some array objects become option types:

Input:

[
    { "id": 1, "name": "Alice", "nickname": "Ali" },
    { "id": 2, "name": "Bob" }
]

Output:

type RootItem = { id: int; name: string; nickname: string option }
type Root = RootItem list

Type Inference Rules

JSON Value

F# Type

"string"

string

123

int

9999999999

int64

123.45

float

true/false

bool

null

obj

[...]

ElementType list

{...}

Record type

Special Field Name Handling

Leading Digits

Fields starting with digits are prefixed with _:

{ "2faEnabled": true }

Generates: _2faEnabled: bool

Reserved Keywords

F# keywords are escaped with double backticks:

{ "type": "admin" }

Generates: ``type``: string

Configuration Reference

Project Properties

Property

Default

Description

FabulousAstOutputFolder

Generated

Output folder for generated files (relative to project directory)

EnableFabulousAstJson

true

Enable/disable generation (set to false to skip)

Note: EnableFabulousAstJson defaults to true, so you only need to set it explicitly if you want to disable generation.

Item Metadata

Metadata

Default

Description

RootName

Root

Root type name

ModuleName

(empty)

File-level module name (e.g., MyApp.Models)

OutputFileName

{InputName}.g.fs

Output filename

Incremental Builds

The task uses content hashing for efficient builds:

IDE Integration

Generated files are automatically included in compilation and placed before your source files, ensuring proper compile order.

IDE Recognition

Since generated files are added at project load time, most IDEs will recognize them after the first build. If your IDE doesn't see the generated types:

  1. Build the project once (dotnet build)
  2. Reload/refresh the project in your IDE

Tips

Troubleshooting

IDE Not Seeing Generated Files

  1. Build the project: dotnet build
  2. Reload/refresh the project in your IDE
  3. Ensure the file exists in the Generated/ folder

File Not Updating

dotnet clean && dotnet build

Disable Generation

Via project property:

<EnableFabulousAstJson>false</EnableFabulousAstJson>

Via command line:

dotnet build -p:EnableFabulousAstJson=false

Next Steps

val id: x: 'T -> 'T
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
type bool = System.Boolean
type 'T list = List<'T>
type 'T option = Option<'T>

Type something to start searching.