jet / FsCodec

F# Event-Union Contract Encoding with versioning tolerant converters supporting System.Text.Json and Newtonsoft.Json
https://github.com/jet/dotnet-templates
Apache License 2.0
83 stars 19 forks source link

Port UnionConverter to STJ #43

Closed NickDarvey closed 4 years ago

NickDarvey commented 4 years ago

WIP reimplementing FsCodec.NewtonsoftJson.UnionConverter on System.Text.Json re #14 feeding into #38

To do

CLAassistant commented 4 years ago

CLA assistant check
All committers have signed the CLA.

bartelink commented 4 years ago

Some generic commentary regarding reasoning behind my comments about the behavior of the converters in FsCodec (but also more broadly) when registered globally:

Especially for UnionConverter my high level opinions are: a) one should not be using unions or fancy things in JSON for simplicity of semantics across lots of consumer languages b) when used, you want to know about it

having that hardline stance also brings some benefits: a) less test cases - just prove it doesnt muck about with stuff not tagged with a converter b) (esp in Equinox, where its chief use is for roundtripping State unions in a state-machine stylee being used as snapshots) forcing the tagging makes people think a bit in the course of moving the state type to be in the module Events in the module Aggregate

I did a commit or two recently in stj to similarly reduce greediness in TypeSafeEnum.isTypeSafeEnum -

I've not expressed this philosophy well in the README as yet, but it also seems to align with how stuff is in STJ in general (vs newtonsoftland where greedier convention based converters are less frowned on)

finally in addition to Explicit being better than Implicit in general, it also tends to work well for perf - i.e. the converter gets detected at type level and then lives in the mappings cache, vs if it is global it is a tax on every type walk that happens not sure if there is an article on this; there should be!

aside: there are alternate views out there - for instance @ylibrach and his team have a wide array of converters, including registering quite a few globally

but bottom line is that in the FsCodec context:

... so UnionConverter being required to be marked explicitly on the concrete union type simply follows from that

NickDarvey commented 4 years ago

the converter gets detected at type level and then lives in the mappings cache, vs if it is global it is a tax on every type walk that happens

I'm not sure if this is what you mean, but they do cache converters created by the factory. Though I guess specifying it as a concrete type saves that first-run reflection still.

... so UnionConverter being required to be marked explicitly on the concrete union type simply follows from that

Understood, less greedy converters makes more sense for STJ and not having them so easily available pushes our users toward being more explicit about their serialization.

I'm going to remove the JsonConverterFactory so our users are pushed toward being more explicit when they're serializing DUs.

bartelink commented 4 years ago

I'm not sure if this is what you mean, but they do cache converters created by the factory. Though I guess specifying it as a concrete type saves that first-run reflection still.

I was referring to the fact that the set of globally registered converters will be consulted per property as it determines the mapping path that'll apply (even if that's cached per type after that).

bartelink commented 4 years ago

Semi related: stumbled on @ylibrach's https://github.com/dotnet/runtime/issues/1130 just now too

bartelink commented 4 years ago

I've had out of band discussions with Nick regarding this PR. The status is as follows:

bartelink commented 3 years ago

Argh! Didnt mean for this PR to be closed - I promise I'll get back on rebasing and merging it soon!

Can you make sure to keep this around for when that time comes please?