Skip to content

Commit 565a168

Browse files
committed
[#180] Allow overriding generic types
1 parent c96f414 commit 565a168

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

src/FSharp.SystemTextJson/Helpers.fs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#nowarn "44" // JsonSerializerOptions.IgnoreNullValues is obsolete for users but still relevant for converters.
44

55
open System
6+
open System.Collections.Generic
67
open System.Reflection
78
open System.Text.Json
89
open System.Text.Json.Serialization
@@ -111,31 +112,38 @@ let rec tryGetNullValue (fsOptions: JsonFSharpOptionsRecord) (ty: Type) : obj vo
111112
let isNullableFieldType (fsOptions: JsonFSharpOptionsRecord) (ty: Type) =
112113
tryGetNullValue fsOptions ty |> ValueOption.isSome
113114

115+
let private tryGetTypeOrGeneric (ty: Type) (types: IDictionary<Type, _>) =
116+
let mutable res = Unchecked.defaultof<_>
117+
if isNull types then
118+
ValueNone
119+
elif types.TryGetValue(ty, &res) then
120+
ValueSome res
121+
elif ty.IsGenericType && types.TryGetValue(ty.GetGenericTypeDefinition(), &res) then
122+
ValueSome res
123+
else
124+
ValueNone
125+
114126
let overrideOptions (ty: Type) (defaultOptions: JsonFSharpOptions) =
115127
let inheritUnionEncoding (options: JsonFSharpOptions) =
116128
if options.UnionEncoding.HasFlag(JsonUnionEncoding.Inherit) then
117129
options.WithUnionEncoding(defaultOptions.UnionEncoding)
118130
else
119131
options
120132

121-
let applyAttributeOverride () =
122-
if defaultOptions.AllowOverride then
123-
match
124-
ty.GetCustomAttributes(typeof<IJsonFSharpConverterAttribute>, true)
125-
|> Array.tryHead
126-
with
127-
| Some (:? IJsonFSharpConverterAttribute as attr) -> attr.Options |> inheritUnionEncoding
128-
| _ -> defaultOptions
133+
let overrides = defaultOptions.Overrides defaultOptions
134+
135+
match tryGetTypeOrGeneric ty overrides with
136+
| ValueSome options ->
137+
inheritUnionEncoding options
138+
| ValueNone when defaultOptions.AllowOverride ->
139+
let attrs = ty.GetCustomAttributes(typeof<IJsonFSharpConverterAttribute>, true)
140+
if attrs.Length > 0 then
141+
let attr = attrs[0] :?> IJsonFSharpConverterAttribute
142+
inheritUnionEncoding attr.Options
129143
else
130144
defaultOptions
131-
132-
let overrides = defaultOptions.Overrides defaultOptions
133-
if isNull overrides then
134-
applyAttributeOverride ()
135-
else
136-
match overrides.TryGetValue(ty) with
137-
| true, options -> options |> inheritUnionEncoding
138-
| false, _ -> applyAttributeOverride ()
145+
| ValueNone ->
146+
defaultOptions
139147

140148
let isWrappedString (ty: Type) =
141149
TypeCache.isUnion ty

tests/FSharp.SystemTextJson.Tests/Test.Union.fs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,39 @@ module NonStruct =
12481248
.ToJsonSerializerOptions()
12491249
Assert.Equal("""{"tag2":"A","x":123,"y":"abc"}""", JsonSerializer.Serialize(Override.A(123, "abc"), o))
12501250

1251+
[<Fact>]
1252+
let ``should apply explicit overrides on generic type`` () =
1253+
let o =
1254+
JsonFSharpOptions
1255+
.Default()
1256+
.WithUnionInternalTag()
1257+
.WithUnionNamedFields()
1258+
.WithOverrides(fun o -> dict [ typedefof<Result<_, _>>, o.WithUnionTagName("Result") ])
1259+
.ToJsonSerializerOptions()
1260+
Assert.Equal("""{"Result":"Ok","ResultValue":"abc"}""", JsonSerializer.Serialize(Ok "abc", o))
1261+
1262+
[<Fact>]
1263+
let ``should apply explicit override on specific type over generic type`` () =
1264+
let o =
1265+
JsonFSharpOptions
1266+
.Default()
1267+
.WithUnionInternalTag()
1268+
.WithUnionNamedFields()
1269+
.WithOverrides(fun o ->
1270+
dict
1271+
[ typeof<Result<string, string>>, o.WithUnionTagName("SpecificResult")
1272+
typedefof<Result<_, _>>, o.WithUnionTagName("GenericResult") ]
1273+
)
1274+
.ToJsonSerializerOptions()
1275+
Assert.Equal(
1276+
"""{"GenericResult":"Ok","ResultValue":42}""",
1277+
JsonSerializer.Serialize((Ok 42: Result<int, string>), o)
1278+
)
1279+
Assert.Equal(
1280+
"""{"SpecificResult":"Ok","ResultValue":"abc"}""",
1281+
JsonSerializer.Serialize((Ok "abc": Result<string, string>), o)
1282+
)
1283+
12511284
type NamedAfterTypesA = NTA of int
12521285

12531286
type NamedAfterTypesB = NTB of int * string

0 commit comments

Comments
 (0)