Skip to content

Commit

Permalink
Add Serdes.DefaultOptions/Settings (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink authored May 5, 2021
1 parent 8f765f7 commit 1db3880
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ The `Unreleased` section name is replaced by the expected version of next releas
## [Unreleased]

### Added

- `NewtonsoftJson.Serdes.DefaultSettings`: Exposes default settings (for use with ASP.NET Core `.AddNewtonsoftJson`) [#63](https://github.com/jet/FsCodec/pull/63)
- `SystemTextJson.Serdes.DefaultOptions`: Exposes default options (for use with ASP.NET Core `.AddJsonOptions`) [#63](https://github.com/jet/FsCodec/pull/63)

### Changed
### Removed
### Fixed
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,46 @@ The respective concrete Codec packages include relevant `Converter`/`JsonConvert
[`FsCodec.NewtonsoftJson.Serdes`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/Serdes.fs#L7) provides light wrappers over `JsonConvert.(Des|S)erializeObject` that utilize the serialization profile defined by `Settings/Options.Create` (above). Methods:
- `Serialize<T>`: serializes an object per its type using the settings defined in `Settings/Options.Create`
- `Deserialize<T>`: deserializes an object per its type using the settings defined in `Settings/Options.Create`
- `DefaultSettings` / `DefaultOptions`: Allows one to access a global static instance of the `JsonSerializerSettings`/`JsonSerializerOptions` used by the default profile.

# Usage of Converters with ASP.NET Core

ASP.NET Core's out-of-the-box behavior is to use `System.Text.Json`. One can explicitly opt to use the more ubiquitous `Newtonsoft.Json` via the `Microsoft.AspNetCore.Mvc.NewtonsoftJson` package's `AddNewtonsoftJson` by adjusting one's `.AddMvc)`.

If you follow the policies covered in the rest of the documentation here, your DTO types (and/or types in your `module Events` that you surface while you are scaffolding and/or hacking without an anti-corruption layer) will fall into one of two classifications:

1. Types that have an associated Converter explicitly annotated (e.g., DU types bear an associated `UnionConverter`, `TypeSafeEnumConverter` or `JsonIsomorphism`-based custom converter, custom types follow the conventions or define a `JsonIsomorphism`-based converter)
2. Types that require a global converter to be registered. _While it may seem that the second set is open-ended and potentially vast, experience teaches that you want to keep it minimal._. This boils down to:
- records arrays and all other good choices for types Just Work already
- `Nullable<MyType>`: Handled out of the box by both NSJ and STJ - requires no converters, provides excellent interop with other CLR languages. Would recommend.
- `MyType option`: Covered by the global `OptionConverter`/`JsonOptionConverter` (see below for a clean way to add them to the default MVC view rendering configuration). Note that while this works well with ASP.NET Core, it may be problematic if you share contracts (yes, not saying you should) or rely on things like Swashbuckle which will need to be aware of the types when they reflect over them.

**The bottom line is that using exotic types in DTOs is something to think very hard about before descending into. The next sections are thus only relevant if you decide to add that extra complexity to your system...**

<a name="aspnetnsj"></a>
## ASP.NET Core with `Newtonsoft.Json`
Hence the following represents the recommended default policy:-

services.AddMvc(fun options -> ...
).AddNewtonsoftJson(fun options ->
FsCodec.NewtonsoftJson.Serdes.DefaultSettings.Converters
|> Seq.iter options.SerializerSettings.Converters.Add
) |> ignore

This adds all the converters used by the default `Serdes` mechanism (currently only `FsCodec.NewtonsoftJson.OptionConverter()`), and add them to any imposed by other configuration logic.

<a name="asnetpstj"></a>
## ASP.NET Core with `System.Text.Json`

The equivalent for the native `System.Text.Json` looks like this:

services.AddMvc(fun options -> ...
).AddJsonOptions(fun options ->
FsCodec.SystemTextJson.Serdes.DefaultOptions.Converters
|> Seq.iter options.JsonSerializerOptions.Converters.Add
) |> ignore

_As of `System.Text.Json` v5, the only converter used under the hood at present is `FsCodec.SystemTextJson.JsonOptionConverter()`_.

# Examples: `FsCodec.(Newtonsoft|SystemText)Json`

Expand Down
7 changes: 5 additions & 2 deletions src/FsCodec.NewtonsoftJson/Serdes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ type Serdes private () =
static let defaultSettings = lazy Settings.Create()
static let indentSettings = lazy Settings.Create(indent = true)

/// Yields the settings used by <c>Serdes</c> when no <c>settings</c> are supplied.
static member DefaultSettings : JsonSerializerSettings = defaultSettings.Value

/// Serializes given value to a JSON string.
static member Serialize<'T>
( /// Value to serialize.
value : 'T,
/// Use indentation when serializing JSON. Defaults to false.
[<Optional; DefaultParameterValue null>] ?indent : bool) : string =
[<Optional; DefaultParameterValue false>] ?indent : bool) : string =
let settings = (if defaultArg indent false then indentSettings else defaultSettings).Value
JsonConvert.SerializeObject(value, settings)
Serdes.Serialize<'T>(value, settings)

/// Serializes given value to a JSON string with custom settings
static member Serialize<'T>
Expand Down
9 changes: 6 additions & 3 deletions src/FsCodec.SystemTextJson/Serdes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,25 @@ type Serdes private () =
static let defaultOptions = lazy Options.Create()
static let indentOptions = lazy Options.Create(indent = true)

/// Yields the settings used by <c>Serdes</c> when no <c>options</c> are supplied.
static member DefaultOptions : JsonSerializerOptions = defaultOptions.Value

/// Serializes given value to a JSON string.
static member Serialize<'T>
( /// Value to serialize.
value : 'T,
/// Use indentation when serializing JSON. Defaults to false.
[<Optional; DefaultParameterValue null>] ?indent : bool) : string =
[<Optional; DefaultParameterValue false>] ?indent : bool) : string =
let options = (if defaultArg indent false then indentOptions else defaultOptions).Value
JsonSerializer.Serialize(value, options)
Serdes.Serialize<'T>(value, options)

/// Serializes given value to a JSON string with custom options
static member Serialize<'T>
( /// Value to serialize.
value : 'T,
/// Options to use (use other overload to use Options.Create() profile)
options : JsonSerializerOptions) : string =
JsonSerializer.Serialize(value, options)
JsonSerializer.Serialize<'T>(value, options)

/// Deserializes value of given type from JSON string.
static member Deserialize<'T>
Expand Down

0 comments on commit 1db3880

Please sign in to comment.