Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ The `Unreleased` section name is replaced by the expected version of next releas
### Added
### Changed
### Removed

- `SystemTextJson.UnionConverter`: Reverted `CanConvert` widening for boxed DU case types [#144](https://github.com/jet/FsCodec/pull/144)

### Fixed

<a name="3.1.1"></a>
Expand Down
2 changes: 1 addition & 1 deletion src/FsCodec.SystemTextJson/UnionConverter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type UnionConverter<'T>() =
let converterOptions = UnionConverterOptions.get typeof<'T>
let info = FsCodec.Union.Info.get typeof<'T>

override _.CanConvert t = (t = typeof<'T> || t.DeclaringType = typeof<'T>) && FsCodec.Union.isUnion t
override _.CanConvert t = t = typeof<'T> && FsCodec.Union.isUnion t

override _.Write(writer: Utf8JsonWriter, value, options: JsonSerializerOptions) =
let value = box value
Expand Down
11 changes: 10 additions & 1 deletion tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -684,14 +684,23 @@ module IsomorphismUnionEncoder =
// NOTE: STJ uses inherit: false when looking up [JsonConverter] attributes, so the attribute on the
// union base type is not found for case-specific nested types. The converter must be registered
// via options.Converters (or autoUnionToJsonObject) for boxed serialization to work.
// This is not recommended practice from a System.Text.Json library perspective; if you need it,
// use a custom JsonConverterFactory as shown here to provide the widened CanConvert logic.
module ``Boxed DU Serialization`` =

/// Custom factory that widens CanConvert to also accept the nested case types that STJ resolves
/// for boxed multi-case DUs (where the runtime type is e.g. Union+Case1 with DeclaringType = Union).
type BoxedUnionConverterFactory<'T>() =
inherit Serialization.JsonConverterFactory()
override _.CanConvert t = (t = typeof<'T> || t.DeclaringType = typeof<'T>) && FsCodec.Union.isUnion t
override _.CreateConverter(_t, _options) = UnionConverter<'T>()

type BoxedMultiCase =
| Case1 of int
| Case2 of string

let optsWithConverter = JsonSerializerOptions()
optsWithConverter.Converters.Add(UnionConverter<BoxedMultiCase>())
optsWithConverter.Converters.Add(BoxedUnionConverterFactory<BoxedMultiCase>())

let [<Fact>] ``Serialize boxed multi-case DU via options does not throw`` () =
let json = JsonSerializer.Serialize(Case1 42 :> obj, optsWithConverter)
Expand Down