From 6144527964afaea9c557025c3c43429399c62467 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Thu, 5 May 2022 17:22:25 +0100 Subject: [PATCH] Remove net461; switch to net6.0 re #60 (#76) --- CHANGELOG.md | 8 ++++ Directory.Build.props | 14 ------- README.md | 42 +++++++++---------- azure-pipelines.yml | 5 +++ global.json | 4 +- src/FsCodec.Box/FsCodec.Box.fsproj | 9 ++-- src/FsCodec.NewtonsoftJson/Codec.fs | 10 ++--- .../FsCodec.NewtonsoftJson.fsproj | 18 ++++---- .../{Settings.fs => Options.fs} | 8 ++-- src/FsCodec.NewtonsoftJson/Serdes.fs | 33 +-------------- .../FsCodec.SystemTextJson.fsproj | 9 ++-- src/FsCodec.SystemTextJson/Serdes.fs | 30 ------------- src/FsCodec/FsCodec.fsproj | 10 ++--- .../FsCodec.NewtonsoftJson.Tests/Examples.fsx | 10 ++--- .../FsCodec.NewtonsoftJson.Tests.fsproj | 6 +-- .../PicklerTests.fs | 4 +- .../SomeNullHandlingTests.fs | 12 +++--- .../UnionConverterTests.fs | 18 ++++---- .../VerbatimUtf8ConverterTests.fs | 2 +- .../FsCodec.SystemTextJson.Tests.fsproj | 7 ++-- .../InteropTests.fs | 2 +- tests/FsCodec.Tests/FsCodec.Tests.fsproj | 7 ++-- 22 files changed, 98 insertions(+), 170 deletions(-) rename src/FsCodec.NewtonsoftJson/{Settings.fs => Options.fs} (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5b7c2..060ca68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,15 @@ The `Unreleased` section name is replaced by the expected version of next releas ### Added ### Changed + +- `NewtonsoftJson`: Rename `Settings` to `Options` [#60](https://github.com/jet/FsCodec/issues/60) [#76](https://github.com/jet/FsCodec/pull/76) +- Updated build and tests to use `net6.0`, all test package dependencies +- Updated `TypeShape` reference to v `10`, triggering min `FSharp.Core` target moving to `4.5.4` + ### Removed + +- `net461` support [#60](https://github.com/jet/FsCodec/issues/60) [#76](https://github.com/jet/FsCodec/pull/76) + ### Fixed diff --git a/Directory.Build.props b/Directory.Build.props index c977c42..1a7b31d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,9 +8,6 @@ Apache-2.0 Copyright © 2016-22 - netcoreapp3.1;net461 - - net5.0 $([System.IO.Path]::GetFullPath("$(MSBuildThisFileDirectory)")) @@ -21,18 +18,7 @@ true true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - false - - - - - - - PreserveNewest - - diff --git a/README.md b/README.md index 52d5869..9f2ab4b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # FsCodec [![Build Status](https://dev.azure.com/jet-opensource/opensource/_apis/build/status/jet.fscodec?branchName=master)](https://dev.azure.com/jet-opensource/opensource/_build/latest?definitionId=18?branchName=master) [![release](https://img.shields.io/github/release/jet/fscodec.svg)](https://github.com/jet/fscodec/releases) [![NuGet](https://img.shields.io/nuget/vpre/fscodec.svg?logo=nuget)](https://www.nuget.org/packages/fscodec/) [![license](https://img.shields.io/github/license/jet/fscodec.svg)](LICENSE) -Defines a minimal interface for serialization and deserialization of events for event-sourcing systems on .NET.\ +Defines a minimal interface for serialization and deserialization of events for event-sourcing systems on .NET. Provides implementation packages for writing simple yet versionable Event Contract definitions in F# using ubiquitous serializers. Typically used in [applications](https://github.com/jet/dotnet-templates) leveraging [Equinox](https://github.com/jet/equinox) and/or [Propulsion](https://github.com/jet/propulsion), but also applicable to defining DTOs for other purposes such as Web APIs. ## Components -The components within this repository are delivered as multi-targeted Nuget packages supporting `net461` (F# 3.1+) and `netstandard2.0`/`1` (F# 4.5+) profiles. +The components within this repository are delivered as multi-targeted Nuget packages supporting `netstandard2.0`/`1` (F# 4.5+) profiles. - [![Codec NuGet](https://img.shields.io/nuget/v/FsCodec.svg)](https://www.nuget.org/packages/FsCodec/) `FsCodec` Defines interfaces with trivial implementation helpers. - No dependencies. @@ -15,13 +15,13 @@ The components within this repository are delivered as multi-targeted Nuget pack - [`FsCodec.Codec`](https://github.com/jet/FsCodec/blob/master/src/FsCodec/Codec.fs#L5): enables plugging in a serializer and/or Union Encoder of your choice (typically this is used to supply a pair `encode` and `tryDecode` functions) - [`FsCodec.StreamName`](https://github.com/jet/FsCodec/blob/master/src/FsCodec/StreamName.fs): strongly-typed wrapper for a Stream Name, together with factory functions and active patterns for parsing same - [![Box Codec NuGet](https://img.shields.io/nuget/v/FsCodec.Box.svg)](https://www.nuget.org/packages/FsCodec.Box/) `FsCodec.Box`: See [`FsCodec.Box.Codec`](#boxcodec); `IEventCodec` implementation that provides a null encode/decode step in order to enable decoupling of serialization/deserialization concerns from the encoding aspect, typically used together with [`Equinox.MemoryStore`](https://www.fuget.org/packages/Equinox.MemoryStore) - - [depends](https://www.fuget.org/packages/FsCodec.Box) on `FsCodec`, `TypeShape >= 8` + - [depends](https://www.fuget.org/packages/FsCodec.Box) on `FsCodec`, `TypeShape >= 10` - [![Newtonsoft.Json Codec NuGet](https://img.shields.io/nuget/v/FsCodec.NewtonsoftJson.svg)](https://www.nuget.org/packages/FsCodec.NewtonsoftJson/) `FsCodec.NewtonsoftJson`: As described in [a scheme for the serializing Events modelled as an F# Discriminated Union](https://eiriktsarpalis.wordpress.com/2018/10/30/a-contract-pattern-for-schemaless-datastores/), enabled tagging of F# Discriminated Union cases in a versionable manner with low-dependencies using [TypeShape](https://github.com/eiriktsarpalis/TypeShape)'s [`UnionContractEncoder`](https://eiriktsarpalis.wordpress.com/2018/10/30/a-contract-pattern-for-schemaless-datastores) - Uses the ubiquitous [`Newtonsoft.Json`](https://github.com/JamesNK/Newtonsoft.Json) library to serialize the event bodies. - Provides relevant Converters for common non-primitive types prevalent in F# - - [depends](https://www.fuget.org/packages/FsCodec.NewtonsoftJson) on `FsCodec`, `Newtonsoft.Json >= 11.0.2`, `TypeShape >= 8`, `Microsoft.IO.RecyclableMemoryStream >= 1.2.2`, `System.Buffers >= 4.5` + - [depends](https://www.fuget.org/packages/FsCodec.NewtonsoftJson) on `FsCodec`, `Newtonsoft.Json >= 11.0.2`, `TypeShape >= 10`, `Microsoft.IO.RecyclableMemoryStream >= 2.2.0`, `System.Buffers >= 4.5.1` - [![System.Text.Json Codec NuGet](https://img.shields.io/nuget/v/FsCodec.SystemTextJson.svg)](https://www.nuget.org/packages/FsCodec.SystemTextJson/) `FsCodec.SystemTextJson`: See [#38](https://github.com/jet/FsCodec/pulls/38): drop in replacement that allows one to retarget from `Newtonsoft.Json` to the .NET Core >= v 3.0 default serializer: `System.Text.Json`, solely by changing the referenced namespace. - - [depends](https://www.fuget.org/packages/FsCodec.SystemTextJson) on `FsCodec`, `System.Text.Json >= 6.0.1`, `TypeShape >= 9` + - [depends](https://www.fuget.org/packages/FsCodec.SystemTextJson) on `FsCodec`, `System.Text.Json >= 6.0.1`, `TypeShape >= 10` # Features: `FsCodec` @@ -93,16 +93,16 @@ The respective concrete Codec packages include relevant `Converter`/`JsonConvert ### `Newtonsoft.Json`-specific low level converters - - [`OptionConverter`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/OptionConverter.fs#L7) represents F#'s `Option<'t>` as a value or `null`; included in the standard `Settings.Create` profile. + - [`OptionConverter`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/OptionConverter.fs#L7) represents F#'s `Option<'t>` as a value or `null`; included in the standard `Options.Create` profile. - [`VerbatimUtf8JsonConverter`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/VerbatimUtf8JsonConverter.fs#L7) captures/renders known valid UTF8 JSON data into a `byte[]` without decomposing it into an object model (not typically relevant for application level code, used in `Equinox.Cosmos` versions prior to `3.0`). ### `System.Text.Json`-specific low level converters - `UnionOrTypeSafeEnumConverterFactory`: Global converter that can apply `TypeSafeEnumConverter` to all Discriminated Unions that do not have cases with values, and `UnionConverter` to ones that have values. See [this `System.Text.Json` issue](https://github.com/dotnet/runtime/issues/55744) for background information as to the reasoning behind and tradeoffs involved in applying such a policy. -## `FsCodec.NewtonsoftJson.Settings` +## `FsCodec.NewtonsoftJson.Options` -[`FsCodec.NewtonsoftJson.Settings`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/Settings.fs#L8) provides a clean syntax for building a `Newtonsoft.Json.JsonSerializerSettings` with which to define a serialization contract profile for interoperability purposes. Methods: +[`FsCodec.NewtonsoftJson.Options`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/Options.fs#L8) provides a clean syntax for building a `Newtonsoft.Json.JsonSerializerSettings` with which to define a serialization contract profile for interoperability purposes. Methods: - `CreateDefault`: as per `Newtonsoft.Json` defaults with the following override: - `DateTimeZoneHandling = DateTimeZoneHandling.Utc` (default is `RoundtripKind`) - no custom `IContractResolver` (one is expected to use `camelCase` field names within records, for which this does not matter) @@ -112,7 +112,7 @@ The respective concrete Codec packages include relevant `Converter`/`JsonConvert ## `FsCodec.SystemTextJson.Options` -[`FsCodec.SystemTextJson.Options`](https://github.com/jet/FsCodec/blob/stj/src/FsCodec.SystemTextJson/Options.fs#L8) provides a clean syntax for building a `System.Text.Json.Serialization.JsonSerializerOptions` as per `FsCodec.NewtonsoftJson.Settings`, above. Methods: +[`FsCodec.SystemTextJson.Options`](https://github.com/jet/FsCodec/blob/stj/src/FsCodec.SystemTextJson/Options.fs#L8) provides a clean syntax for building a `System.Text.Json.Serialization.JsonSerializerOptions` as per `FsCodec.NewtonsoftJson.Options`, above. Methods: - `CreateDefault`: configures the settings equivalent to `new JsonSerializerSettings()` or `JsonSerializerSettings.Default`, without overrides of any kind (see `Create`, below for the relevant differences) - `Create`: as `CreateDefault` with the following difference: - By default, inhibits the HTML-safe escaping that `System.Text.Json` provides as a default by overriding `Encoder` with `System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping` @@ -123,9 +123,9 @@ The respective concrete Codec packages include relevant `Converter`/`JsonConvert ## `Serdes` -[`FsCodec.SystemTextJson/NewtonsoftJson.Serdes`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.SystemTextJson/Serdes.fs#L7) provides light wrappers over `(JsonConvert|JsonSerializer).(Des|S)erialize(Object)?` based on an explicitly supplied serialization profile created by `Settings/Options.Create` (above), or using `Settings/Options.Default`. This enables one to smoothly switch between `System.Text.Json` vs `Newtonsoft.Json` serializers with minimal application code changes, while also ensuring consistent and correct options get applied in each case. Methods: -- `Serialize`: serializes an object per its type using the settings defined in `Settings/Options.Create` -- `Deserialize`: deserializes an object per its type using the settings defined in `Settings/Options.Create` +[`FsCodec.SystemTextJson/NewtonsoftJson.Serdes`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.SystemTextJson/Serdes.fs#L7) provides light wrappers over `(JsonConvert|JsonSerializer).(Des|S)erialize(Object)?` based on an explicitly supplied serialization profile created by `Options.Create` (above), or using `Options.Default`. This enables one to smoothly switch between `System.Text.Json` vs `Newtonsoft.Json` serializers with minimal application code changes, while also ensuring consistent and correct options get applied in each case. Methods: +- `Serialize`: serializes an object per its type using the settings defined in `Options.Create` +- `Deserialize`: deserializes an object per its type using the settings defined in `Options.Create` - `Options`: Allows one to access the `JsonSerializerSettings`/`JsonSerializerOptions` used by this instance. # Usage of Converters with ASP.NET Core @@ -147,13 +147,13 @@ If you follow the policies covered in the rest of the documentation here, your D Hence the following represents the recommended default policy:- /// Define a Serdes instance with a given policy somewhere (globally if you need to do explicit JSON generation) - let serdes = Serdes Settings.Default + let serdes = Serdes Options.Default services.AddMvc(fun options -> ... ).AddNewtonsoftJson(fun options -> // Borrow the Converters from the Options the Serdes is holding serdes.Options.Converters |> Seq.iter options.SerializerSettings.Converters.Add - // OR, in the trivial case: Settings.Default.Converters |> Seq.iter options.SerializerSettings.Converters.Add + // OR, in the trivial case: Options.Default.Converters |> Seq.iter options.SerializerSettings.Converters.Add ) |> ignore This adds all the converters used by the `serdes` serialization/deserialization policy (currently only `FsCodec.NewtonsoftJson.OptionConverter`) into the equivalent managed by ASP.NET. @@ -195,7 +195,7 @@ The minimal code needed to define helpers to consistently roundtrip where one on ```fsharp module Contract = type Item = { value : string option } - /// Settings to be used within this contract (opinionated ones compared to just using JsonSerializer.Serialize / Deserialize) + /// Options to be used within this contract (opinionated ones compared to just using JsonSerializer.Serialize / Deserialize) let private serdes = FsCodec.SystemTextJson.Serdes FsCodec.SystemTextJson.Default // implies default settings from Options.Create(), i.e., includes UnsafeRelaxedJsonEscaping let serialize (x : Item) : string = serdes.Serialize x @@ -210,7 +210,7 @@ While it's hard to justify the wrapping in the previous case, this illustrates h ```fsharp module Contract = type Item = { Value : string option; other : TypeThatRequiresMyCustomConverter } - /// Settings to be used within this contract - note the Pascal Cased Value propertu compared to the previous record definition + /// Options to be used within this contract - note the Pascal Cased Value property compared to the previous record definition let private serdes = FsCodec.SystemTextJson.Options.Create(converters = [| MyCustomConverter() |], camelCase = true) |> FsCodec.SystemTextJson.Serdes let serialize (x : Item) = serdes.Serialize x let deserialize (json : string) : Item = serdes.Deserialize json @@ -225,16 +225,16 @@ module Contract = Normal primitive F#/.NET such as `bool`, `byte`, `int16`, `int`, `int64`, `float32` (`Single`), `float` (`Double`), `decimal` work as expected. -The default settings for FsCodec applies Json.NET's default behavior, whis is to render fields that have a `null` or `null`-equivalent value with the value `null`. This behavior can be overridden via `Settings(ignoreNulls = true)`, which will cause such JSON fields to be omitted. +The default settings for FsCodec applies Json.NET's default behavior, which is to render fields that have a `null` or `null`-equivalent value with the value `null`. This behavior can be overridden via `Options(ignoreNulls = true)`, which will cause such JSON fields to be omitted. -The recommendations here apply particularly to Event Contracts - the data in your store will inevitably outlast your code, so being conservative in the complexity of ones's encoding scheme is paramount. Explicit is better than Implicit. +The recommendations here apply particularly to Event Contracts - the data in your store will inevitably outlast your code, so being conservative in the complexity of one's encoding scheme is paramount. Explicit is better than Implicit. | Type kind | TL;DR | Notes | Example input | Example output | | :--- | :--- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| :--- | :--- | | `'t[]` | As per C# | Don't forget to handle `null` | `[ 1; 2; 3]` | `[1,2,3]` | -| `DateTimeOffset` | Roundtrips cleanly | The default `Settings.Create` requests `RoundtripKind` | `DateTimeOffset.Now` | `"2019-09-04T20:30:37.272403+01:00"` | -| `Nullable<'t>` | As per C#; `Nullable()` -> `null`, `Nullable x` -> `x` | OOTB Json.NET and STJ roundtrip cleanly. Works with `Settings.CreateDefault()`. Worth considering if your contract does not involve many `option` types | `Nullable 14` | `14` | -| `'t option` | `Some null`,`None` -> `null`, `Some x` -> `x` _with the converter `Settings.Create()` adds_ | OOTB Json.NET does not roundtrip `option` types cleanly; `Settings.Create` wires in an `OptionConverter` by default in `FsCodec.NewtonsoftJson`
NOTE `Some null` will produce `null`, but deserialize as `None` - i.e., it's not round-trippable | `Some 14` | `14` | +| `DateTimeOffset` | Roundtrips cleanly | The default `Options.Create` requests `RoundtripKind` | `DateTimeOffset.Now` | `"2019-09-04T20:30:37.272403+01:00"` | +| `Nullable<'t>` | As per C#; `Nullable()` -> `null`, `Nullable x` -> `x` | OOTB Json.NET and STJ roundtrip cleanly. Works with `Options.CreateDefault()`. Worth considering if your contract does not involve many `option` types | `Nullable 14` | `14` | +| `'t option` | `Some null`,`None` -> `null`, `Some x` -> `x` _with the converter `Options.Create()` adds_ | OOTB Json.NET does not roundtrip `option` types cleanly; `Options.Create` wires in an `OptionConverter` by default in `FsCodec.NewtonsoftJson`
NOTE `Some null` will produce `null`, but deserialize as `None` - i.e., it's not round-trippable | `Some 14` | `14` | | `string` | As per C#; need to handle `null` | One can use a `string option` to map `null` and `Some null` to `None` | `"Abc"` | `"Abc"` | | types with unit of measure | Works well (doesnt encode the unit) | Unit of measure tags are only known to the compiler; Json.NET does not process the tags and treats it as the underlying primitive type | `54` | `54` | | [`FSharp.UMX`](https://github.com/fsprojects/FSharp.UMX) tagged `string`, `DateTimeOffset` | Works well | [`FSharp.UMX`](https://github.com/fsprojects/FSharp.UMX) enables one to type-tag `string` and `DateTimeOffset` values using the units of measure compiler feature, which Json.NET will render as if they were unadorned | `SkuId.parse "54-321"` | `"000-054-321"` | diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 27a709f..f93c30f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,6 +37,11 @@ jobs: pool: vmImage: 'macOS-latest' steps: + - task: UseDotNet@2 + displayName: 'Install .NET 6 sdk' + inputs: + packageType: sdk + version: 6.x - script: dotnet test build.proj displayName: dotnet test - task: PublishTestResults@2 diff --git a/global.json b/global.json index 532472e..e1b2476 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "5.0.200", - "rollForward": "latestFeature" + "version": "6.0.202", + "rollForward": "latestMajor" } } diff --git a/src/FsCodec.Box/FsCodec.Box.fsproj b/src/FsCodec.Box/FsCodec.Box.fsproj index e9d5313..c9a0135 100644 --- a/src/FsCodec.Box/FsCodec.Box.fsproj +++ b/src/FsCodec.Box/FsCodec.Box.fsproj @@ -2,7 +2,6 @@ netstandard2.0 - false true true @@ -12,12 +11,12 @@ - - + + - + - + diff --git a/src/FsCodec.NewtonsoftJson/Codec.fs b/src/FsCodec.NewtonsoftJson/Codec.fs index a6986ca..181789f 100755 --- a/src/FsCodec.NewtonsoftJson/Codec.fs +++ b/src/FsCodec.NewtonsoftJson/Codec.fs @@ -54,7 +54,7 @@ module Core = /// See for example usage. type Codec private () = - static let defaultSettings = lazy Settings.Create() + static let defaultSettings = lazy Options.Create() /// Generate an IEventCodec using the supplied Newtonsoft.Json settings.
/// Uses up and down functions to facilitate upconversion/downconversion @@ -70,7 +70,7 @@ type Codec private () = /// a meta object that will be serialized with the same settings (if it's not None) /// and an Event Creation timestamp.
down : 'Context option * 'Event -> 'Contract * 'Meta option * Guid * string * string * DateTimeOffset option, - /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Settings.Default + /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Options.Default [] ?settings, /// Enables one to fail encoder generation if union contains nullary cases. Defaults to false, i.e. permitting them [] ?rejectNullaryCases) @@ -116,7 +116,7 @@ type Codec private () = down : 'Event -> 'Contract * 'Meta option * DateTimeOffset option, /// Uses the 'Context passed to the Encode call and the 'Meta emitted by down to a) the final metadata b) the correlationId and c) the correlationId mapCausation : 'Context option * 'Meta option -> 'Meta option * Guid * string * string, - /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Settings.Default + /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Options.Default [] ?settings, /// Enables one to fail encoder generation if union contains nullary cases. Defaults to false, i.e. permitting them [] ?rejectNullaryCases) @@ -142,7 +142,7 @@ type Codec private () = /// a meta object that will be serialized with the same settings (if it's not None) /// and an Event Creation timestamp. down : 'Event -> 'Contract * 'Meta option * DateTimeOffset option, - /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Settings.Default + /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Options.Default [] ?settings, /// Enables one to fail encoder generation if union contains nullary cases. Defaults to false, i.e. permitting them [] ?rejectNullaryCases) @@ -155,7 +155,7 @@ type Codec private () = /// The Event Type Names are inferred based on either explicit DataMember(Name= Attributes, or (if unspecified) the Discriminated Union Case Name /// 'Union must be tagged with interface TypeShape.UnionContract.IUnionContract to signify this scheme applies. static member Create<'Union when 'Union :> TypeShape.UnionContract.IUnionContract> - ( /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Settings.Default + ( /// Configuration to be used by the underlying Newtonsoft.Json Serializer when encoding/decoding. Defaults to same as Options.Default [] ?settings, /// Enables one to fail encoder generation if union contains nullary cases. Defaults to false, i.e. permitting them [] ?rejectNullaryCases) diff --git a/src/FsCodec.NewtonsoftJson/FsCodec.NewtonsoftJson.fsproj b/src/FsCodec.NewtonsoftJson/FsCodec.NewtonsoftJson.fsproj index 6f9d760..53e43f3 100644 --- a/src/FsCodec.NewtonsoftJson/FsCodec.NewtonsoftJson.fsproj +++ b/src/FsCodec.NewtonsoftJson/FsCodec.NewtonsoftJson.fsproj @@ -1,8 +1,7 @@  - netstandard2.0;net461 - false + netstandard2.0 true true @@ -12,23 +11,22 @@ - +
- - + + - - + - + - - + + diff --git a/src/FsCodec.NewtonsoftJson/Settings.fs b/src/FsCodec.NewtonsoftJson/Options.fs similarity index 96% rename from src/FsCodec.NewtonsoftJson/Settings.fs rename to src/FsCodec.NewtonsoftJson/Options.fs index 7492fe5..4ab31a5 100755 --- a/src/FsCodec.NewtonsoftJson/Settings.fs +++ b/src/FsCodec.NewtonsoftJson/Options.fs @@ -5,13 +5,13 @@ open Newtonsoft.Json.Serialization open System open System.Runtime.InteropServices -type Settings private () = +type Options private () = static let defaultConverters : JsonConverter[] = [| OptionConverter() |] - static let def = lazy Settings.Create() + static let def = lazy Options.Create() - /// Analogous to JsonSerializerOptions.Default - allows for sharing/caching of the default profile as defined by Settings.Create() + /// Analogous to JsonSerializerOptions.Default - allows for sharing/caching of the default profile as defined by Options.Create() static member Default : JsonSerializerSettings = def.Value /// Creates a default set of serializer settings used by Json serialization. When used with no args, same as JsonSerializerSettings.CreateDefault() @@ -60,7 +60,7 @@ type Settings private () = /// Error on missing values (as opposed to letting them just be default-initialized); defaults to false [] ?errorOnMissing : bool) = - Settings.CreateDefault( + Options.CreateDefault( converters = (match converters with null | [||] -> defaultConverters | xs -> Array.append defaultConverters xs), ?ignoreNulls = ignoreNulls, ?errorOnMissing = errorOnMissing, diff --git a/src/FsCodec.NewtonsoftJson/Serdes.fs b/src/FsCodec.NewtonsoftJson/Serdes.fs index aed129d..5cb686f 100755 --- a/src/FsCodec.NewtonsoftJson/Serdes.fs +++ b/src/FsCodec.NewtonsoftJson/Serdes.fs @@ -1,10 +1,8 @@ namespace FsCodec.NewtonsoftJson -open FsCodec.NewtonsoftJson open Newtonsoft.Json -open System.Runtime.InteropServices -/// Serializes to/from strings using the supplied Settings +/// Serializes to/from strings using the supplied JsonSerializerSettings type Serdes(options : JsonSerializerSettings) = /// The JsonSerializerSettings used by this instance. @@ -17,32 +15,3 @@ type Serdes(options : JsonSerializerSettings) = /// Deserializes value of given type from JSON string. member x.Deserialize<'T>(json : string) : 'T = JsonConvert.DeserializeObject<'T>(json, options) - - /// Serializes given value to a JSON string. - [] - static member Serialize<'T> - ( /// Value to serialize. - value : 'T, - /// Use indentation when serializing JSON. Defaults to false. - [] ?indent : bool) : string = - let options = (if indent = Some true then Settings.Create(indent = true) else Settings.Default) - JsonConvert.SerializeObject(value, options) - - /// Serializes given value to a JSON string with custom options - [] - static member Serialize<'T> - ( /// Value to serialize. - value : 'T, - /// Settings to use (use other overload to use Settings.Default profile) - settings : JsonSerializerSettings) : string = - JsonConvert.SerializeObject(value, settings) - - /// Deserializes value of given type from JSON string. - [] - static member Deserialize<'T> - ( /// Json string to deserialize. - json : string, - /// Settings to use (defaults to Settings.Default profile) - [] ?settings : JsonSerializerSettings) : 'T = - let settings = match settings with Some x -> x | None -> Settings.Default - JsonConvert.DeserializeObject<'T>(json, settings) diff --git a/src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj b/src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj index d933ff5..531e94a 100644 --- a/src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj +++ b/src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj @@ -2,7 +2,6 @@ netstandard2.1 - false true true @@ -19,13 +18,13 @@ - - + + - + - + diff --git a/src/FsCodec.SystemTextJson/Serdes.fs b/src/FsCodec.SystemTextJson/Serdes.fs index f4ded5a..629c206 100755 --- a/src/FsCodec.SystemTextJson/Serdes.fs +++ b/src/FsCodec.SystemTextJson/Serdes.fs @@ -1,6 +1,5 @@ namespace FsCodec.SystemTextJson -open System.Runtime.InteropServices open System.Text.Json /// Serializes to/from strings using the supplied Options @@ -16,32 +15,3 @@ type Serdes(options : JsonSerializerOptions) = /// Deserializes value of given type from JSON string. member x.Deserialize<'T>(json : string) : 'T = JsonSerializer.Deserialize<'T>(json, options) - - /// Serializes given value to a JSON string. - [] - static member Serialize<'T> - ( /// Value to serialize. - value : 'T, - /// Use indentation when serializing JSON. Defaults to false. - [] ?indent : bool) : string = - let options = (if indent = Some true then Options.Create(indent = true) else Options.Default) - JsonSerializer.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.Default profile) - options : JsonSerializerOptions) : string = - JsonSerializer.Serialize<'T>(value, options) - - /// Deserializes value of given type from JSON string. - [] - static member Deserialize<'T> - ( /// Json string to deserialize. - json : string, - /// Options to use (defaults to Options.Default profile) - [] ?options : JsonSerializerOptions) : 'T = - let settings = match options with Some o -> o | None -> Options.Default - JsonSerializer.Deserialize<'T>(json, settings) diff --git a/src/FsCodec/FsCodec.fsproj b/src/FsCodec/FsCodec.fsproj index 05f8b0e..735edb8 100644 --- a/src/FsCodec/FsCodec.fsproj +++ b/src/FsCodec/FsCodec.fsproj @@ -1,8 +1,7 @@  - netstandard2.0;net461 - false + netstandard2.0 true true @@ -14,11 +13,10 @@ - - + + - - + diff --git a/tests/FsCodec.NewtonsoftJson.Tests/Examples.fsx b/tests/FsCodec.NewtonsoftJson.Tests/Examples.fsx index e50dbce..adb3c30 100755 --- a/tests/FsCodec.NewtonsoftJson.Tests/Examples.fsx +++ b/tests/FsCodec.NewtonsoftJson.Tests/Examples.fsx @@ -23,7 +23,7 @@ module Contract = type Item = { value : string option } // implies an OptionConverter will be applied - let private serdes = Serdes Settings.Default + let private serdes = Serdes Options.Default let serialize (x : Item) : string = serdes.Serialize x let deserialize (json : string) = serdes.Deserialize json @@ -32,13 +32,13 @@ module Contract2 = type TypeThatRequiresMyCustomConverter = { mess : int } type MyCustomConverter() = inherit JsonPickler() override _.Read(_,_) = "" override _.Write(_,_,_) = () type Item = { Value : string option; other : TypeThatRequiresMyCustomConverter } - /// Settings to be used within this contract + /// Options to be used within this contract // note OptionConverter is also included by default; Value field will write as `"value"` - let private serdes = Settings.Create(MyCustomConverter(), camelCase = true) |> FsCodec.NewtonsoftJson.Serdes + let private serdes = Options.Create(MyCustomConverter(), camelCase = true) |> FsCodec.NewtonsoftJson.Serdes let serialize (x : Item) = serdes.Serialize x let deserialize (json : string) : Item = serdes.Deserialize json -let private serdes = Settings.Default |> Serdes +let private serdes = Options.Default |> Serdes let inline ser x = serdes.Serialize(x) let inline des<'t> x = serdes.Deserialize<'t>(x) @@ -61,7 +61,7 @@ ser { a = "testing"; b = Guid.Empty } ser Guid.Empty // "00000000-0000-0000-0000-000000000000" -let serdesWithGuidConverter = Settings.Create(converters = [| GuidConverter() |]) |> Serdes +let serdesWithGuidConverter = Options.Create(converters = [| GuidConverter() |]) |> Serdes serdesWithGuidConverter.Serialize(Guid.Empty) // 00000000000000000000000000000000 diff --git a/tests/FsCodec.NewtonsoftJson.Tests/FsCodec.NewtonsoftJson.Tests.fsproj b/tests/FsCodec.NewtonsoftJson.Tests/FsCodec.NewtonsoftJson.Tests.fsproj index d60041d..9d02fa5 100644 --- a/tests/FsCodec.NewtonsoftJson.Tests/FsCodec.NewtonsoftJson.Tests.fsproj +++ b/tests/FsCodec.NewtonsoftJson.Tests/FsCodec.NewtonsoftJson.Tests.fsproj @@ -1,8 +1,7 @@  - $(TestTargetFrameworks) - false + net6.0 @@ -17,8 +16,7 @@ - - + diff --git a/tests/FsCodec.NewtonsoftJson.Tests/PicklerTests.fs b/tests/FsCodec.NewtonsoftJson.Tests/PicklerTests.fs index add62ea..7b49be1 100644 --- a/tests/FsCodec.NewtonsoftJson.Tests/PicklerTests.fs +++ b/tests/FsCodec.NewtonsoftJson.Tests/PicklerTests.fs @@ -36,8 +36,8 @@ let [] ``Tagging with GuidConverter`` () = let [] ``Global GuidConverter`` () = let value = Guid.Empty - let resDashes = JsonConvert.SerializeObject(value, Settings.Default) - let resNoDashes = JsonConvert.SerializeObject(value, Settings.Create(GuidConverter())) + let resDashes = JsonConvert.SerializeObject(value, Options.Default) + let resNoDashes = JsonConvert.SerializeObject(value, Options.Create(GuidConverter())) test <@ "\"00000000-0000-0000-0000-000000000000\"" = resDashes && "\"00000000000000000000000000000000\"" = resNoDashes @> diff --git a/tests/FsCodec.NewtonsoftJson.Tests/SomeNullHandlingTests.fs b/tests/FsCodec.NewtonsoftJson.Tests/SomeNullHandlingTests.fs index b8136bd..0d695f8 100644 --- a/tests/FsCodec.NewtonsoftJson.Tests/SomeNullHandlingTests.fs +++ b/tests/FsCodec.NewtonsoftJson.Tests/SomeNullHandlingTests.fs @@ -21,16 +21,16 @@ open FsCodec.NewtonsoftJson open Swensen.Unquote open Xunit -let ootb = Settings.CreateDefault() |> Serdes -let serdes = Serdes Settings.Default +let ootb = Options.CreateDefault() |> Serdes +let serdes = Serdes Options.Default -let [] ``Settings.CreateDefault roundtrips null string option, but rendering is ugly`` () = +let [] ``Options.CreateDefault roundtrips null string option, but rendering is ugly`` () = let value : string option = Some null let ser = ootb.Serialize value test <@ ser = "{\"Case\":\"Some\",\"Fields\":[null]}" @> test <@ value = ootb.Deserialize ser @> -let [] ``Settings.Create does not roundtrip Some null`` () = +let [] ``Options.Create does not roundtrip Some null`` () = let value : string option = Some null let ser = serdes.Serialize value "null" =! ser @@ -58,7 +58,7 @@ let [] ``Workaround is to detect and/or substitute such non-roundtrippable type RecordWithStringOptions = { x : int; y : Nested } and Nested = { z : string option } -let [] ``Can detect and/or substitute null string option when using Options/Settings.Create`` () = +let [] ``Can detect and/or substitute null string option when using Options.Create`` () = let value : RecordWithStringOptions = { x = 9; y = { z = Some null } } test <@ hasSomeNull value @> let value = replaceSomeNullsWithNone value @@ -72,7 +72,7 @@ let [] ``Can detect and/or substitute null string option when using Option let ignoreNullsSerdes = Options.Create(ignoreNulls = true) |> Serdes #else // As one might expect, the ignoreNulls setting is also honored - let ignoreNullsSerdes = Settings.Create(ignoreNulls = true) |> Serdes + let ignoreNullsSerdes = Options.Create(ignoreNulls = true) |> Serdes #endif let ser = ignoreNullsSerdes.Serialize value ser =! """{"x":9,"y":{}}""" diff --git a/tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs b/tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs index 4ca6781..da56538 100644 --- a/tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs +++ b/tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs @@ -88,7 +88,7 @@ let assertIgnoreNullsIs value (profile : JsonSerializerOptions) = profile.DefaultIgnoreCondition =! if value then JsonIgnoreCondition.Always else JsonIgnoreCondition.Never #else let serializeWith<'t> (profile : JsonSerializerSettings) (value : 't) = JsonConvert.SerializeObject(value, profile) -let settings = Settings.Create(camelCase = false, ignoreNulls = true) +let settings = Options.Create(camelCase = false, ignoreNulls = true) let serializeDefault<'t> value = serializeWith<'t> settings value let deserializeWith<'t> (profile : JsonSerializerSettings) serialized = JsonConvert.DeserializeObject<'t>(serialized, profile) @@ -168,8 +168,8 @@ let ``deserializes properly`` () = #if SYSTEM_TEXT_JSON let requiredSettingsToHandleOptionalFields = Options.Default #else - // This is equivalent to Settings.Default, but we want absolutely minimal adjustment from the out-of-the-box Newtonsoft settings - let requiredSettingsToHandleOptionalFields = Settings.CreateDefault(OptionConverter()) + // This is equivalent to Options.Default, but we want absolutely minimal adjustment from the out-of-the-box Newtonsoft settings + let requiredSettingsToHandleOptionalFields = Options.CreateDefault(OptionConverter()) #endif let deserializeCustom s = deserializeWith requiredSettingsToHandleOptionalFields s test <@ CaseM (Some 1) = deserializeCustom """{"case":"CaseM","a":1}""" @> @@ -186,8 +186,8 @@ module MissingFieldsHandling = let rejectMissingSettings = [ JsonSerializerSettings(MissingMemberHandling = MissingMemberHandling.Error) - Settings.CreateDefault(errorOnMissing=true) - Settings.Create(errorOnMissing=true)] + Options.CreateDefault(errorOnMissing=true) + Options.Create(errorOnMissing=true)] [] let ``lets converters reject missing values by feeding them a null`` () = @@ -235,7 +235,7 @@ let (|Q|) (s: string) = JsonSerializer.Serialize(s, defaultOptions) // Renderings when NullValueHandling=Include, which is used by the recommended Options.Create profile #else let (|Q|) (s: string) = Newtonsoft.Json.JsonConvert.SerializeObject s -// Renderings when NullValueHandling=Include, which is the default for Json.net, and used by the recommended Settings.CreateCorrect profile +// Renderings when NullValueHandling=Include, which is the default for Json.net, and used by the recommended Options.Create profile #endif let render ignoreNulls = function | CaseA { test = null } when ignoreNulls -> """{"case":"CaseA"}""" @@ -323,7 +323,7 @@ let roundtripProperty ignoreNulls profile value = #if SYSTEM_TEXT_JSON let includeNullsProfile = Options.Create(ignoreNulls = false) #else -let includeNullsProfile = Settings.CreateDefault(OptionConverter() (*, ignoreNulls=false*)) +let includeNullsProfile = Options.CreateDefault(OptionConverter() (*, ignoreNulls=false*)) #endif [] let ``UnionConverter includeNulls Profile roundtrip property test`` (x: TestDU) = @@ -334,7 +334,7 @@ let ``UnionConverter includeNulls Profile roundtrip property test`` (x: TestDU) #if SYSTEM_TEXT_JSON let defaultProfile = Options.Default #else -let defaultProfile = Settings.Default +let defaultProfile = Options.Default #endif [] let ``UnionConverter opinionated Profile roundtrip property test`` (x: TestDU) = @@ -542,7 +542,7 @@ module ``Struct discriminated unions`` = #if SYSTEM_TEXT_JSON let serdes = Serdes Options.Default #else -let serdes = Serdes Settings.Default +let serdes = Serdes Options.Default #endif module Nested = diff --git a/tests/FsCodec.NewtonsoftJson.Tests/VerbatimUtf8ConverterTests.fs b/tests/FsCodec.NewtonsoftJson.Tests/VerbatimUtf8ConverterTests.fs index 78e7c35..70f8aa0 100644 --- a/tests/FsCodec.NewtonsoftJson.Tests/VerbatimUtf8ConverterTests.fs +++ b/tests/FsCodec.NewtonsoftJson.Tests/VerbatimUtf8ConverterTests.fs @@ -70,7 +70,7 @@ module VerbatimUtf8Tests = // not a module or CI will fail for net461 let decoded = eventCodec.TryDecode loaded |> Option.get input =! decoded - let defaultSettings = Settings.CreateDefault() + let defaultSettings = Options.CreateDefault() let defaultEventCodec = Codec.Create(defaultSettings) let [] ``round-trips diverse bodies correctly`` (x: U) = diff --git a/tests/FsCodec.SystemTextJson.Tests/FsCodec.SystemTextJson.Tests.fsproj b/tests/FsCodec.SystemTextJson.Tests/FsCodec.SystemTextJson.Tests.fsproj index 41c41eb..baea105 100644 --- a/tests/FsCodec.SystemTextJson.Tests/FsCodec.SystemTextJson.Tests.fsproj +++ b/tests/FsCodec.SystemTextJson.Tests/FsCodec.SystemTextJson.Tests.fsproj @@ -1,15 +1,14 @@  - net5.0 - false + net6.0 SYSTEM_TEXT_JSON - - + + diff --git a/tests/FsCodec.SystemTextJson.Tests/InteropTests.fs b/tests/FsCodec.SystemTextJson.Tests/InteropTests.fs index 2fed622..8acc16e 100644 --- a/tests/FsCodec.SystemTextJson.Tests/InteropTests.fs +++ b/tests/FsCodec.SystemTextJson.Tests/InteropTests.fs @@ -32,7 +32,7 @@ type U = | N interface TypeShape.UnionContract.IUnionContract -let defaultSettings = FsCodec.NewtonsoftJson.Settings.CreateDefault() // Test without converters, as that's what Equinox.Cosmos will do +let defaultSettings = FsCodec.NewtonsoftJson.Options.CreateDefault() // Test without converters, as that's what Equinox.Cosmos will do let defaultEventCodec = FsCodec.NewtonsoftJson.Codec.Create(defaultSettings) let indirectCodecU = FsCodec.SystemTextJson.Codec.Create() |> FsCodec.SystemTextJson.InteropExtensions.ToByteArrayCodec diff --git a/tests/FsCodec.Tests/FsCodec.Tests.fsproj b/tests/FsCodec.Tests/FsCodec.Tests.fsproj index c3774cc..5dcedff 100644 --- a/tests/FsCodec.Tests/FsCodec.Tests.fsproj +++ b/tests/FsCodec.Tests/FsCodec.Tests.fsproj @@ -1,8 +1,7 @@  - $(TestTargetFrameworks) - false + net6.0 @@ -10,8 +9,8 @@ - - + +