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
-[](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.
24
-
-[depends](https://www.fuget.org/packages/FsCodec.SystemTextJson) on `FsCodec`, `System.Text.Json >= 4.7.0`, `TypeShape >= 8`
24
+
-[depends](https://www.fuget.org/packages/FsCodec.SystemTextJson) on `FsCodec`, `System.Text.Json >= 5.0.0-preview.3`, `TypeShape >= 8`
25
25
26
26
Deltas in behavior/functionality vs `FsCodec.NewtonsoftJson`:
27
27
@@ -70,13 +70,12 @@ While this may not seem like a sufficiently large set of converters for a large
70
70
71
71
### ... but don't forget `FSharp.SystemTextJson`
72
72
73
-
NOTE `System.Text.Json`, esp in the .NET Core 3 era is very spartan, especially wrt F# support: it doesnt support unions, options, lists or records out of the box. Its hoped that over time the base support in there will improve and the shimming FsCodec does (e.g. `JsonRecordConverter`) can be reduced. It's worth calling out explicitly that there are no plans to extend the representations `FsCodec.SystemTextJson` can handle in any significant way over time - if you have specific exotic corner cases and determine you need something more specifically tailored, the Converters abstraction affords you ability to mix and match from the [`FSharp.SystemTextJson`](https://github.com/Tarmil/FSharp.SystemTextJson) library - it provides a much broader and complete (and well tested) set of converters with a broader remit than what FsCodec is trying to maintain as its sweet spot.
73
+
`System.Text.Json` v `4.x` did not even support F# records that are not marked `[<CLIMutable>]` out-of-the-box (it was similarly spartan wrt C# types, requiring a default constructor on `class`es). The `>= 5.0` that `FsCodec.System.Text.Json` requires does support records, but it doesnt support Discriminated Unions, `option`s, `list`s, `Set` or `Map` out of the box. It's worth calling out explicitly that there are no plans to extend the representations `FsCodec.SystemTextJson` can handle in any significant way over time ([the advice for `FsCodec.NewtonsoftJson` has always been to avoid stuff outside of records, `option`s and `array`s](#recommendations)) - if you have specific exotic corner cases and determine you need something more specifically tailored, the Converters abstraction affords you ability to mix and match from the [`FSharp.SystemTextJson`](https://github.com/Tarmil/FSharp.SystemTextJson) library - it provides a much broader and complete (and well tested) set of converters with a broader remit than what FsCodec is trying to maintain as its sweet spot.
74
74
75
75
### Core converters
76
76
77
77
The respective concrete Codec packages include relevant `Converter`/`JsonConverter` in order to facilitate interoperable and versionable renderings:
78
78
-`JsonOptionConverter` / [`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`/`Options.Create` profile. `System.Text.Json` reimplementation :pray:[@ylibrach](https://github.com/ylibrach)
79
-
-[`JsonRecordConverter`](https://github.com/jet/FsCodec/blob/stj/src/FsCodec.SystemTextJson/JsonRecordConverter.fs#L18) represents F# records as a JSON Object; included in the standard `Options.Create` profile [as [`System.Text.Json` does not support F# records out of the box](https://github.com/dotnet/runtime/issues/29812)]. :pray:[@ylibrach](https://github.com/ylibrach)
80
79
-[`TypeSafeEnumConverter`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/TypeSafeEnumConverter.fs#L33) represents discriminated union (whose cases are all nullary), as a `string` in a trustworthy manner (`Newtonsoft.Json.Converters.StringEnumConverter` permits values outside the declared values) :pray:[@amjjd](https://github.com/amjjd)
81
80
-[`UnionConverter`](https://github.com/jet/FsCodec/blob/master/src/FsCodec.NewtonsoftJson/UnionConverter.fs#L71) represents F# discriminated unions as a single JSON `object` with both the tag value and the body content as named fields directly within :pray:[@amjdd](https://github.com/amjjd); `System.Text.Json` reimplementation :pray:[@NickDarvey](https://github.com/NickDarvey)
82
81
@@ -107,7 +106,7 @@ The respective concrete Codec packages include relevant `Converter`/`JsonConvert
107
106
[`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:
108
107
-`CreateDefault`: equivalent to generating a `new JsonSerializerSettings()` without any overrides of any kind
109
108
-`Create`: as `CreateDefault` with the following difference:
110
-
- adds a `JsonOptionConverter` and a `JsonRecordConverter`; included in default `Settings` (see _Converters_, below)
109
+
- adds a `JsonOptionConverter`; included in default `Settings` (see _Converters_, below)
111
110
- Inhibits the HTML-safe escaping that `System.Text.Json` provides as a default by overriding `Encoder` with `System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping`
112
111
113
112
## `Serdes`
@@ -159,7 +158,7 @@ module Contract =
159
158
<aname="recommendations"></a>
160
159
### Recommended round-trippable constructs
161
160
162
-
`Newtonsoft.Json`, thanks to its broad usage thoughout .NET systems has well known (with some idiosyncratic quirks) behaviors for most common types one might use for C# DTOs.
161
+
`Newtonsoft.Json`, thanks to its broad usage throughout .NET systems has well known (with some idiosyncratic quirks) behaviors for most common types one might use for C# DTOs.
163
162
164
163
Normal primitive F#/.NET such as `bool`, `byte`, `int16`, `int`, `int64`, `float32` (`Single`), `float` (`Double`), `decimal` work as expected.
165
164
@@ -176,7 +175,7 @@ The recommendations here apply particularly to Event Contracts - the data in you
176
175
|`string`| As per C#; need to handle `null`| One can use a `string option` to map `null` and `Some null` to `None`|`"Abc"`|`"Abc"`|
177
176
| 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<g>`|`54`|
178
177
|[`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"`|
179
-
| records | Just work | For `System.Text.Json`, there's a `JsonRecordConverter` in the standard `Options`, as records are not supported out of the box|`{\| a = 1; b = Some "x" \|}`|`"{"a":1,"b":"x"}"`|
178
+
| records | Just work | For `System.Text.Json` v `4.x`, usage of `[<CLIMutable>]` or a custom `JsonRecordConverter` was once required|`{\| a = 1; b = Some "x" \|}`|`"{"a":1,"b":"x"}"`|
180
179
| Nullary unions (Enum-like DU's without bodies) | Tag `type` with `TypeSafeEnumConverter`| Works well - guarantees a valid mapping, as opposed to using a `System.Enum` and `StringEnumConverter`, which can map invalid values and/or silently map to `0` etc |`State.NotFound`|`"NotFound"`|
181
180
| Discriminated Unions (where one or more cases has a body) | Tag `type` with `UnionConverter`| This format can be readily consumed in Java, JavaScript and Swift. Nonetheless, exhaust all other avenues before considering encoding a union in JSON. The `"case"` label id can be overridden. |`Decision.Accepted { result = "54" }`|`{"case": "Accepted","result":"54"}`|
182
181
@@ -187,10 +186,10 @@ The mechanisms in the previous section have proven themselves sufficient for div
187
186
| Type kind | TL;DR | Example input | Example output | Notes |
188
187
| :--- | :--- | :--- | :--- | :--- |
189
188
|`'t list`|__Don't use__; use `'t[]`|`[ 1; 2; 3]`|`[1,2,3]`| While the happy path works, `null` or missing field maps to a `null` object rather than `[]`[which is completely wrong from an F# perspective]|
190
-
|`DateTime`|__Don't use__; use `DateTimeOffset`|||Roundtripping can be messy, wrong or lossy; `DateTimeOffset` covers same use cases |
189
+
|`DateTime`|__Don't use__; use `DateTimeOffset`|||Round-tripping can be messy, wrong or lossy; `DateTimeOffset` covers same use cases |
191
190
|`Guid` or [`FSharp.UMX`](https://github.com/fsprojects/FSharp.UMX) tagged `Guid`|__don't use__; wrap as a reference `type` and use a `JsonIsomorphism`, or represent as a tagged `string`|`Guid.NewGuid()`|`"ba7024c7-6795-413f-9f11-d3b7b1a1fe7a"`| If you wrap the value in a type, you can have that roundtrip with a specific format via a Converter implemented as a `JsonIsomorphism`. Alternately, represent in your contract as a [`FSharp.UMX`](https://github.com/fsprojects/FSharp.UMX) tagged-string. |
192
-
| maps/`Dictionary` etc. | avoid; prefer arrays ||| As per C#; not always the best option for many reasons, both on the producer and consumer side. Json.NET has support for various maps with various idiosyncracies typically best covered by Stack Overflow, but often a list of records is clearer |
193
-
| tuples |__Don't use__; use records |`(1,2)`|`{"Item1":1,"Item2":2}`| While converters are out there, using tuples in contracts ofany kind is simply Not A Good Idea |
191
+
| maps/`Dictionary` etc. | avoid; prefer arrays ||| As per C#; not always the best option for many reasons, both on the producer and consumer side. Json.NET has support for various maps with various idiosyncracies typically best covered by Stack Overflow, but often a list of records is clearer<br/>For `System.Text.Json`, use an `IDictionary<'K, 'V>` or `Dictionary<'K, 'V>`|
192
+
| tuples |__Don't use__; use records |`(1,2)`|`{"Item1":1,"Item2":2}`| While converters are out there, using tuples in contracts of any kind is simply Not A Good Idea |
0 commit comments