You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This library provides F# union and record support for[System.Text.Json](https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/).
6
+
This library provides support for F# types to[System.Text.Json](https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/).
7
7
8
-
## Usage
8
+
It adds support for the following types:
9
9
10
-
The NuGet package is [`FSharp.SystemTextJson`](https://nuget.org/packages/FSharp.SystemTextJson). However the namespace is `System.Text.Json.Serialization` like the base library.
10
+
* F# records (including struct records and anonymous records);
11
11
12
-
There are two ways to use `FSharp.SystemTextJson`: apply it to all F# types by passing `JsonSerializerOptions`, or apply it to specific types with an attribute.
12
+
*F# discriminated unions (including struct unions), with a variety of representations;
Add `JsonFSharpConverter` to the converters in `JsonSerializerOptions`, and the format will be applied to all F# types.
16
+
It provides a number of customization options, allowing a wide range of JSON serialization formats.
17
17
18
-
```fsharp
19
-
open System.Text.Json
20
-
open System.Text.Json.Serialization
18
+
## Documentation
21
19
22
-
let options = JsonSerializerOptions()
23
-
options.Converters.Add(JsonFSharpConverter())
20
+
*[How to use FSharp.SystemTextJson](docs/Using.md)
24
21
25
-
JsonSerializer.Serialize({| x = "Hello"; y = "world!" |}, options)
26
-
// --> {"x":"Hello","y":"world!"}
27
-
```
22
+
*[Serialization format](docs/Format.md)
28
23
29
-
### Using attributes
30
-
31
-
Add `JsonFSharpConverterAttribute` to the type that needs to be serialized.
32
-
33
-
```fsharp
34
-
open System.Text.Json
35
-
open System.Text.Json.Serialization
36
-
37
-
[<JsonFSharpConverter>]
38
-
type Example = { x: string; y: string }
39
-
40
-
JsonSerializer.Serialize({ x = "Hello"; y = "world!" })
41
-
// --> {"x":"Hello","y":"world!"}
42
-
```
43
-
44
-
### Advantages and inconvenients
45
-
46
-
The options way is generally recommended because it applies the format to all F# types. In addition to your defined types, this also includes:
47
-
48
-
* Types defined in referenced libraries that you can't modify to add an attribute. This includes standard library types such as `option` and `Result`.
49
-
* Anonymous records.
50
-
51
-
The attribute way cannot handle the above cases.
52
-
53
-
The advantage of the attribute way is that it allows calling `Serialize` and `Deserialize` without having to pass options every time. This is particularly useful if you are passing your own data to a library that calls these functions itself and doesn't take options.
54
-
55
-
## Using with ASP.NET Core
56
-
57
-
ASP.NET Core can be easily configured to use FSharp.SystemTextJson.
58
-
59
-
### ASP.NET Core MVC
60
-
61
-
To use F# types in MVC controllers, add the following to your startup `ConfigureServices`:
62
-
63
-
```fsharp
64
-
member this.ConfigureServices(services: IServiceCollection) =
65
-
services.AddControllersWithViews() // or whichever method you're using to get an IMvcBuilder
Records and anonymous records are serialized as JSON objects.
189
-
190
-
```fsharp
191
-
type Example = { x: string; y: string }
192
-
193
-
JsonSerializer.Serialize({ x = "Hello"; y = "world!" }, options)
194
-
// --> {"x":"Hello","y":"world!"}
195
-
196
-
JsonSerializer.Serialize({| x = "Hello"; y = "world!" |}, options)
197
-
// --> {"x":"Hello","y":"world!"}
198
-
```
199
-
200
-
Named record fields are serialized in the order in which they were declared in the type declaration.
201
-
202
-
Anonymous record fields are serialized in alphabetical order.
203
-
204
-
### Unions
205
-
206
-
Unions can be serialized in a number of formats. The enum `JsonUnionEncoding` defines the format to use; you can pass a value of this type to the constructor of `JsonFSharpConverter` or to the `JsonFSharpConverter` attribute.
*`JsonUnionEncoding.AdjacentTag` is the default format. It represents unions as a JSON object with two fields:
243
-
244
-
* A field named from [`unionTagName`](#union-field-names): a string whose value is the name of the union case.
245
-
* A field named from [`unionFieldsName`](#union-field-names): an array whose items are the arguments of the union case. This field is absent if the union case has no arguments.
* `JsonUnionEncoding.AdjacentTag ||| JsonUnionEncoding.NamedFields` is similar, except that the fields are represented as an object instead of an array. The field names on this object are the names of the arguments.
Note that if an argument doesn't have an explicit name, F# automatically gives it the name `Item` (if it's the only argument of its case) or `Item1`/`Item2`/etc (if the case has multiple arguments).
272
-
273
-
* `JsonUnionEncoding.ExternalTag` represents unions as a JSON object with one field, whose name is the name of the union case, and whose value is an array whose items are the arguments of the union case.
* `JsonUnionEncoding.ExternalTag ||| JsonUnionEncoding.NamedFields` is similar, except that the fields are represented as an object instead of an array.
* `JsonUnionEncoding.InternalTag ||| JsonUnionEncoding.NamedFields` represents unions as an object whose first field is `unionTagName` (defaulting to `"Case"`), and whose value is the name of the case. The rest of the fields have the names and values of the arguments.
* `JsonUnionEncoding.Untagged` represents unions as an object whose fields have the names and values of the arguments. The name of the case is not encoded at all. Deserialization is only possible if the fields of all cases have different names.
* Additionally, or-ing `||| JsonUnionEncoding.UnwrapFieldlessTags` to any of the previous formats represents cases that don't have any arguments as a simple string.
// --> (same format as without UnwrapFieldlessTags)
336
-
```
337
-
338
-
Union cases that are represented as `null` in .NET using `CompilationRepresentationFlags.UseNullAsTrueValue`, such as `Option.None`, are serialized as `null`.
339
-
340
-
### Options
341
-
342
-
By default, the type `'T option` is treated specially.
343
-
344
-
* The value `None`, as mentioned above, is represented as `null`.
345
-
346
-
* The value `Some x` is represented the same as `x`, without wrapping it in the union representation for `Some`.
347
-
348
-
When using a custom `JsonUnionEncoding`, this behavior is enabled by or-ing `||| JsonUnionEncoding.UnwrapOption`.
24
+
*[Customizing the format](docs/Customizing.md)
349
25
350
26
## FAQ
351
27
352
-
* Does FSharp.SystemTextJson support struct records and unions?
353
-
354
-
Yes!
355
-
356
-
* Does FSharp.SystemTextJson support anonymous records?
357
-
358
-
Yes!
359
-
360
28
* Does FSharp.SystemTextJson support alternative formats for unions?
361
29
362
-
[Yes!](#unions)
30
+
[Yes!](docs/Customizing.md)
363
31
364
32
* Does FSharp.SystemTextJson support representing `'T option` as either just `'T` or `null` (or an absent field)?
As little as possible, but unfortunately the `FSharp.Reflection` API requires some allocations. In particular, an array is allocated for as many items as the record fields or union arguments, and structs are boxed. There is [work in progress](https://github.com/Tarmil/FSharp.SystemTextJson/pull/15) to improve this.
380
52
381
53
* Are there any benchmarks, e.g. against Newtonsoft.Json?
0 commit comments