thoth-org / Thoth.Json

Library for working with JSON in a type safe manner, this libs is targeting Fable
https://thoth-org.github.io/Thoth.Json/
MIT License
152 stars 36 forks source link

Q: Transparently support erased single-case DUs on .NET side #42

Open MangelMaxime opened 4 years ago

MangelMaxime commented 4 years ago

Issue by et1975 Wednesday Aug 14, 2019 at 15:14 GMT Originally opened as https://github.com/MangelMaxime/Thoth/issues/158


Trying to use Thoth.Json on a model I'm sharing with Saturn backend. So I plugged in Thoth as IJsonSerializer and everything seems to be working, however, I'm trying to optimize the use of single-case DUs and I'm applying [<Erase>] on the Fable side and [<Struct>] on the .NET side but that doesn't survive the roundtrip:

#if FABLE_COMPILER
type NoAlloc = Fable.Core.EraseAttribute
#else
type NoAlloc = StructAttribute
#endif

[<NoAlloc>]
type AttributeId = AttributeId of Guid

I guess I need to plug some "extra" encoder/decoder to tell Thoth to unwrap single-case DUs?

MangelMaxime commented 4 years ago

Comment by MangelMaxime Friday Aug 23, 2019 at 10:15 GMT


Hello,

what's the JSON output of .Net side when applying the encoder to [<Struct>]?

MangelMaxime commented 4 years ago

Comment by et1975 Friday Aug 23, 2019 at 12:31 GMT


Struct types retain the shape of their non-struct types, so currently it looks like any other DU:

["AttributeId","00000000-0000-0000-0000-000000000000"]

Edit: Basically it seems an unnecessary overhead to put the single-case DU's case into the payload, but I understand it may be surprising to someone. Perhaps on .NET side Thoth could have an encoder that's not plugged in by default, or only plugged in for [<NoAlloc>] single-case DUs (the attribute Thoth.Net would have to define)? Actually, NoAlloc would have to be the alias like I show here, so introducing such type would only make the confusion worse - someone using Struct would get this behavior as well, so maybe have it on for Struct single-case DUs by default or have it as an optional encoder?

MangelMaxime commented 4 years ago

Comment by MangelMaxime Friday Aug 23, 2019 at 19:23 GMT


Ok, so I need to do more test on my side because I don't see why it's not working by default. I guess the Struct attributes is doing something to the representation of the DUs and/or break the reflection code. I never worked with Struct before and so we don't have tests for them.

Basically it seems an unnecessary overhead to put the single-case DU's case into the payload, but I understand it may be surprising to someone.

This is not the first time someone brought this subject about JSON representation.

It has been brought when Fable.Converter was a thing (Fable 1).

And at F# Exchange, I had a discussion about it too.

I never really looked into this issue because it just impacts the JSON representation and keeps it "constant". But yes, it's making it a bit longer than needed for single-case DUs.

The complaints come when people do "strict" domain modelling like:

type AttributeId = AttributeId of Guid

type Email = Email of string

because from what I understand they consider it an alias to string in the case of Email for example.

I several ways to activate this simplifier solution:

What do you think?

MangelMaxime commented 4 years ago

Comment by et1975 Friday Aug 23, 2019 at 19:46 GMT


Probably better to have an option somewhere.

piaste commented 4 years ago

Probably better to have an option somewhere.

It could be productive to take note of the options provided by FSharp.SystemTextJson - supporting the same formats (maybe even with the same names) would make the ecosystem more friendly. Users would be able to switch between the two libraries, or even use different libraries in different services, without running into trivial incompatibilities.

MangelMaxime commented 4 years ago

I need to check this option, but every library makes some choices about how to represents the type on JSON, so it's hard to have a standard way. Mainly, because there are pros and cons for every design :)

That's one of the reasons why we provide Thoth.Json and Thoth.Json.Net. It guarantees that JSON encoded on the server can be decoded on the client and vice-versa.

ChrisBallard commented 4 years ago

I've just noticed this thread, having come here with the same question relating to the use of single case DUs to add strength to a domain model.

I'd like to add one more use case here also - the situation where we have a server side domain model being served over an API to both a F#/Fable based client, but also to non F# clients. It would be useful to have the option where the single case DUs are just serialized as the underlying type, so they can be trivially deserialized in the non F# world, but also be deserialized back to the strict domain types in the F# client.