Tarmil / FSharp.SystemTextJson

System.Text.Json extensions for F# types
MIT License
325 stars 44 forks source link

Map<string, _> where the string-key has a unit of measure doesn't (de)serialize correctly #40

Closed drhumlen closed 4 years ago

drhumlen commented 4 years ago

We use FSharp.UMX to add some type-safety/specificity to our primitive types – like string.

[<Measure>] type userId

let usersAge: Map<string<userId>, int> = Map [%"user1", 30; %"user2", 40]

However it doesn't serialize as I'd hope: {"user1": 30, "user2": 40}, but instead as [["user1", 30], "user2", 40]

I've looked at the code here:

    static member internal CreateConverter(typeToConvert: Type) =
        let genArgs = typeToConvert.GetGenericArguments()
        let ty =
            if genArgs.[0] = typeof<string> then
                typedefof<JsonStringMapConverter<_>>
                    .MakeGenericType([|genArgs.[1]|])

Is there anything we can do to get the un-tagged version of the type so that it deserializes correctly?

Something like:

if genArgs.[0].UnderlyingSystemType = typeof<string> then ?

drhumlen commented 4 years ago

Upon further investigation it seems like the problem was caused by the tagged type being a Guid, and not a string as thought initially.

Perhaps there should be an issue created for using Guids as key in a map though?

bartelink commented 4 years ago

I believe this to be a general issue with STJ

(In Newtonsoft, TypeConverters can be used as well as the more direct Conversion route (pretty sure that's not on the roadmap for STJ, and would not particularly want it to be).

For me, using FSharp.UMX with Guids is a slightly dangerous thing - with Guid's you have the "N" (aka no dashes) rendering vs the default (and questions about whether you'd accept an input in either form). For Explicit is Better than Implicit reasons, I'd tend to employ a Converter, and then manage the value internally (post conversion) as canonicalized string like this (though probably as a ValueType in an STJ world)