Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call extra encoders for type with generic params #174

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
58 changes: 29 additions & 29 deletions src/Decode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ module Decode =
System.Enum.Parse(t, toString enumValue)
|> Ok
| false ->
(path, BadPrimitiveExtra(t.FullName, value, "Unkown value provided for the enum"))
(path, BadPrimitiveExtra(t.FullName, value, "Unknown value provided for the enum"))
|> Error
| Error msg ->
Error msg
Expand Down Expand Up @@ -1171,14 +1171,14 @@ module Decode =
else mixedArray 1 decoders path values
values |> Result.map (fun values -> FSharpValue.MakeUnion(uci, List.toArray values, allowAccessToPrivateRepresentation=true))

and private autoDecodeRecordsAndUnions extra (caseStrategy : CaseStrategy) (isOptional : bool) (t: System.Type) : BoxedDecoder =
and private autoDecodeRecordsAndUnions extra (caseStrategy : CaseStrategy) (isOptional : bool) (t: System.Type) : BoxedDecoder =
// Add the decoder to extra in case one of the fields is recursive
let decoderRef = ref Unchecked.defaultof<_>
let extra =
// As of 3.7.17 Fable assigns empty name to anonymous record, we shouldn't add them to the map to avoid conflicts.
// Anonymous records cannot be recursive anyways, see #144
match t.FullName with
| "" -> extra
match getTypeName t with
| TypeName "" -> extra
| fullName -> extra |> Map.add fullName decoderRef
let decoder =
if FSharpType.IsRecord(t, allowAccessToPrivateRepresentation=true) then
Expand Down Expand Up @@ -1216,8 +1216,8 @@ Documentation available at: https://thoth-org.github.io/Thoth.Json/documentation
decoderRef.Value <- decoder
decoder

and private autoDecoder (extra: Map<string, ref<BoxedDecoder>>) caseStrategy (isOptional : bool) (t: System.Type) : BoxedDecoder =
let fullname = t.FullName
and private autoDecoder (extra: Map<TypeName, ref<BoxedDecoder>>) caseStrategy (isOptional : bool) (t: System.Type) : BoxedDecoder =
let fullname = getTypeName t
match Map.tryFind fullname extra with
| Some decoderRef -> fun path value -> decoderRef.contents path value
| None ->
Expand Down Expand Up @@ -1259,21 +1259,21 @@ If you can't use one of these types, please pass an extra decoder.
|> Result.map (fun xs -> FSharpValue.MakeTuple(List.toArray xs, t))
else (path, BadPrimitive ("an array", value)) |> Error
else
let fullname = t.GetGenericTypeDefinition().FullName
if fullname = typedefof<obj option>.FullName then
let fullname = getTypeName t
if fullname = getTypeName typedefof<obj option> then
t.GenericTypeArguments.[0] |> (autoDecoder extra caseStrategy true) |> option |> boxDecoder
elif fullname = typedefof<obj list>.FullName then
elif fullname = getTypeName typedefof<obj list> then
t.GenericTypeArguments.[0] |> (autoDecoder extra caseStrategy false) |> list |> boxDecoder
elif fullname = typedefof<obj seq>.FullName then
elif fullname = getTypeName typedefof<obj seq> then
t.GenericTypeArguments.[0] |> (autoDecoder extra caseStrategy false) |> seq |> boxDecoder
elif fullname = typedefof< Map<System.IComparable, obj> >.FullName then
elif fullname = getTypeName typedefof< Map<System.IComparable, obj> > then
let keyDecoder = t.GenericTypeArguments.[0] |> autoDecoder extra caseStrategy false
let valueDecoder = t.GenericTypeArguments.[1] |> autoDecoder extra caseStrategy false
oneOf [
autoObject2 keyDecoder valueDecoder
list (tuple2 keyDecoder valueDecoder)
] |> map (fun ar -> toMap (unbox ar) |> box)
elif fullname = typedefof< Set<string> >.FullName then
elif fullname = getTypeName typedefof< Set<string> > then
let decoder = t.GenericTypeArguments.[0] |> autoDecoder extra caseStrategy false
fun path value ->
match array decoder path value with
Expand All @@ -1282,29 +1282,29 @@ If you can't use one of these types, please pass an extra decoder.
else
autoDecodeRecordsAndUnions extra caseStrategy isOptional t
else
if fullname = typeof<bool>.FullName then
if fullname = getTypeName typeof<bool> then
boxDecoder bool
elif fullname = typedefof<unit>.FullName then
elif fullname = getTypeName typedefof<unit> then
boxDecoder unit
elif fullname = typeof<string>.FullName then
elif fullname = getTypeName typeof<string> then
boxDecoder string
elif fullname = typeof<char>.FullName then
elif fullname = getTypeName typeof<char> then
boxDecoder char
elif fullname = typeof<sbyte>.FullName then
elif fullname = getTypeName typeof<sbyte> then
boxDecoder sbyte
elif fullname = typeof<byte>.FullName then
elif fullname = getTypeName typeof<byte> then
boxDecoder byte
elif fullname = typeof<int16>.FullName then
elif fullname = getTypeName typeof<int16> then
boxDecoder int16
elif fullname = typeof<uint16>.FullName then
elif fullname = getTypeName typeof<uint16> then
boxDecoder uint16
elif fullname = typeof<int>.FullName then
elif fullname = getTypeName typeof<int> then
boxDecoder int
elif fullname = typeof<uint32>.FullName then
elif fullname = getTypeName typeof<uint32> then
boxDecoder uint32
elif fullname = typeof<float>.FullName then
elif fullname = getTypeName typeof<float> then
boxDecoder float
elif fullname = typeof<float32>.FullName then
elif fullname = getTypeName typeof<float32> then
boxDecoder float32
// These number types require extra libraries in Fable. To prevent penalizing
// all users, extra decoders (withInt64, etc) must be passed when they're needed.
Expand All @@ -1317,15 +1317,15 @@ If you can't use one of these types, please pass an extra decoder.
// boxDecoder bigint
// elif fullname = typeof<decimal>.FullName then
// boxDecoder decimal
elif fullname = typeof<System.DateTime>.FullName then
elif fullname = getTypeName typeof<System.DateTime> then
boxDecoder datetimeUtc
elif fullname = typeof<System.DateTimeOffset>.FullName then
elif fullname = getTypeName typeof<System.DateTimeOffset> then
boxDecoder datetimeOffset
elif fullname = typeof<System.TimeSpan>.FullName then
elif fullname = getTypeName typeof<System.TimeSpan> then
boxDecoder timespan
elif fullname = typeof<System.Guid>.FullName then
elif fullname = getTypeName typeof<System.Guid> then
boxDecoder guid
elif fullname = typeof<obj>.FullName then
elif fullname = getTypeName typeof<obj> then
fun _ v -> Ok v
else autoDecodeRecordsAndUnions extra caseStrategy isOptional t

Expand Down
42 changes: 21 additions & 21 deletions src/Encode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,8 @@ module Encode =
let extra =
// As of 3.7.17 Fable assigns empty name to anonymous record, we shouldn't add them to the map to avoid conflicts.
// Anonymous records cannot be recursive anyways, see #144
match t.FullName with
| "" -> extra
match getTypeName t with
| TypeName "" -> extra
| fullName -> extra |> Map.add fullName encoderRef
let encoder =
if FSharpType.IsRecord(t, allowAccessToPrivateRepresentation=true) then
Expand Down Expand Up @@ -438,8 +438,8 @@ Documentation available at: https://thoth-org.github.io/Thoth.Json/documentation
encoderRef.Value <- encoder
encoder

and private autoEncoder (extra: Map<string, ref<BoxedEncoder>>) caseStrategy (skipNullField : bool) (t: System.Type) : BoxedEncoder =
let fullname = t.FullName
and private autoEncoder (extra: Map<TypeName, ref<BoxedEncoder>>) caseStrategy (skipNullField : bool) (t: System.Type) : BoxedEncoder =
let fullname = getTypeName t
match Map.tryFind fullname extra with
| Some encoderRef -> fun v -> encoderRef.contents v
| None ->
Expand Down Expand Up @@ -522,29 +522,29 @@ Documentation available at: https://thoth-org.github.io/Thoth.Json/documentation
else
autoEncodeRecordsAndUnions extra caseStrategy skipNullField t
else
if fullname = typeof<bool>.FullName then
if fullname = getTypeName typeof<bool> then
boxEncoder bool
elif fullname = typeof<unit>.FullName then
elif fullname = getTypeName typeof<unit> then
boxEncoder unit
elif fullname = typeof<string>.FullName then
elif fullname = getTypeName typeof<string> then
boxEncoder string
elif fullname = typeof<char>.FullName then
elif fullname = getTypeName typeof<char> then
boxEncoder char
elif fullname = typeof<sbyte>.FullName then
elif fullname = getTypeName typeof<sbyte> then
boxEncoder sbyte
elif fullname = typeof<byte>.FullName then
elif fullname = getTypeName typeof<byte> then
boxEncoder byte
elif fullname = typeof<int16>.FullName then
elif fullname = getTypeName typeof<int16> then
boxEncoder int16
elif fullname = typeof<uint16>.FullName then
elif fullname = getTypeName typeof<uint16> then
boxEncoder uint16
elif fullname = typeof<int>.FullName then
elif fullname = getTypeName typeof<int> then
boxEncoder int
elif fullname = typeof<uint32>.FullName then
elif fullname = getTypeName typeof<uint32> then
boxEncoder uint32
elif fullname = typeof<float>.FullName then
elif fullname = getTypeName typeof<float> then
boxEncoder float
elif fullname = typeof<float32>.FullName then
elif fullname = getTypeName typeof<float32> then
boxEncoder float32
// These number types require extra libraries in Fable. To prevent penalizing
// all users, extra encoders (withInt64, etc) must be passed when they're needed.
Expand All @@ -557,15 +557,15 @@ Documentation available at: https://thoth-org.github.io/Thoth.Json/documentation
// boxEncoder bigint
// elif fullname = typeof<decimal>.FullName then
// boxEncoder decimal
elif fullname = typeof<System.DateTime>.FullName then
elif fullname = getTypeName typeof<System.DateTime> then
boxEncoder datetime
elif fullname = typeof<System.DateTimeOffset>.FullName then
elif fullname = getTypeName typeof<System.DateTimeOffset> then
boxEncoder datetimeOffset
elif fullname = typeof<System.TimeSpan>.FullName then
elif fullname = getTypeName typeof<System.TimeSpan> then
boxEncoder timespan
elif fullname = typeof<System.Guid>.FullName then
elif fullname = getTypeName typeof<System.Guid> then
boxEncoder guid
elif fullname = typeof<obj>.FullName then
elif fullname = getTypeName typeof<obj> then
boxEncoder id
else
autoEncodeRecordsAndUnions extra caseStrategy skipNullField t
Expand Down
2 changes: 1 addition & 1 deletion src/Extra.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let inline internal withCustomAndKey (encoder: Encoder<'Value>) (decoder: Decode
{ extra with
Hash = System.Guid.NewGuid().ToString()
Coders =
extra.Coders |> Map.add typeof<'Value>.FullName (Encode.boxEncoder encoder, Decode.boxDecoder decoder) }
extra.Coders |> Map.add (getTypeName typeof<'Value>) (Encode.boxEncoder encoder, Decode.boxDecoder decoder) }

let inline withCustom (encoder: Encoder<'Value>) (decoder: Decoder<'Value>) (extra: ExtraCoders): ExtraCoders =
withCustomAndKey encoder decoder extra
Expand Down
10 changes: 10 additions & 0 deletions src/Helpers.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Thoth.Json

[<AutoOpen>]
module internal Helpers =

let getTypeName (t: System.Type) : TypeName =
(t.GetGenericTypeDefinition()).FullName
|> TypeName

let typeNameToString (TypeName str) = str
1 change: 1 addition & 0 deletions src/Thoth.Json.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ If you are interested on using it against .NET Core or .NET Framework, please us
</PropertyGroup>
<ItemGroup>
<Compile Include="Types.fs" />
<Compile Include="Helpers.fs" />
<Compile Include="Decode.fs" />
<Compile Include="Encode.fs" />
<Compile Include="Extra.fs" />
Expand Down
5 changes: 4 additions & 1 deletion src/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ type BoxedDecoder = Decoder<obj>

type BoxedEncoder = Encoder<obj>

/// Represents the `.GetGenericTypeDefinition().FullName` of a type
type TypeName = TypeName of string

type ExtraCoders =
{ Hash: string
Coders: Map<string, BoxedEncoder * BoxedDecoder> }
Coders: Map<TypeName, BoxedEncoder * BoxedDecoder> }

module internal Util =
open System.Collections.Generic
Expand Down