-
Notifications
You must be signed in to change notification settings - Fork 44
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
Support naming union fields from type names where field names are missing #108
Comments
Random devil's advocate questions that do not require a response, but can be addressed by editing the OP... Is the fact that the mapped type name is PascalCase an intentional hint for humans, or would it be better for it to get camelCased (or would you look to have a way to parameterise the mapping rule) ? If this is about strongly typed ids, I personally would use FSharp.UMX, which would have a horribly useless type name ( |
All good questions. I will address them in the light of my use-case, which as mentioned is more readable JSON serialization in logs. In other words, only developers will see the JSON, and the exact formatting isn't critical. The TL;DR is: Keep it simple – it's still a very helpful feature.
While in most cases, I'd ideally like camelCase for consistency with the idiomatic style for manual DU field names, I don't think it's a good idea. The feature then suddenly becomes more complicated. You can't just transform the first letter to lower-case, because then you could end up with weird casings like
IDs is one use-case for single-case DUs, but I use single-case DUs for more or less all primitives. I'd say that only 10% of my single-case DUs are IDs. The rest are other domain "primitives" like mobile numbers, email addresses, and much else that all require some form of validation and often guarantee some invariants. I don't use FSharp.UMX, but I'm OK with fields typed as
I'm not sure what you're asking here. If it's important for you that I respond to this, could you re-phrase the question? In any case, I believe that my fairly simple suggestion is helpful in light of my use-case. It's just intended to reduce the amount of noise needed for helpful DU field names. It doesn't have to be perfect or cater to every edge case. Also, there's always the possibility of specifying the field name manually, or using a record if you really care about naming and consistency/stability of the output. |
If you are rendering Range of int * int, then your basic rules would suggest that could become Int321 or Item1 and Int3222 - was just seeking to get you to expand on your rules with the aim of either making them more general or making you question them.
I think you've done a great job in the above of conveying what your goals are - I guess the lib is not far off being able to deliver on them, even though it's probably some distance from it's core use cases, which I'll be interested to see the maintainer's views on (IMO, based on working with FsCodec and event rendering, having dynamic rendering that can jump around based on small source changes are the last thing you want in a serialization lib, but absolutely what you're after when trying to render stuff for human dev/ops perusal). (Am also selfishly secretly wishing there'll be a revival of Destructurama.FSharp, which I believe is where this sort of pretty/sensible rendering of literals for logging purposes could live if only someone had the time and energy to invest! Easier said than done but surely some TypeShape-fu could yield a nice general lib without too much code ;) ) |
With my rules, they would be Int321 and Int322. Alternatively we can append an underscore: Int32_1 and Int32_2. They aren't great names in any case, but that's fine by me, because if you want better names, you just specify the field names. This option is just a very simple one (simple both to implement and understand) that would remove the need for 90% of manual field names in my use-case, so it's worth it IMHO.
It's the last thing you want when you want stable output, but serialization libraries cater to other use-cases, too. Many of the
Yep, I wanted that, too. Currently it doesn't work right and uses uncached reflection. My journey to this issue started with me first trying to come up with a new kind of Destructurama.FSharp-type library but butting my head against the myriad of ways unions can be encoded and not wanting to duplicate everything in FSharp.SystemTextJson. Then I figured I could piggyback on top of FSharp.SystemTextJson's capabilities with this IDestructuringPolicy, but limitations on the output format for destructured objects (specifically, the lack of indentation) pointed to manual serialization/formatting (no destructuring) being the only viable option for me for the time being (as mentioned by the maintainer in the subsequent comment). |
Yeah, I think this is a reasonable feature to add. The output for built-in types would indeed be a bit odd, but since this is opt-in, I'm fine with leaving the user responsible for using meaningful type names. For the camelCase/PascalCase issue, luckily there's a built-in solution in STJ. The option > JsonNamingPolicy.CamelCase.ConvertName("IPAddress");;
val it: string = "ipAddress" So you just have to use |
That's interesting! But I guess there is no simple way to use this only for union fields? |
@cmeeren That could be another new feature. |
That sounds like a good way to go about it. 👍 |
This has been added in v0.18. |
First of all, thank you for an excellent library. It has become a staple in all my F# work.
Description
I propose adding an option to name union fields according to the name of the type in that field (specifically, the
System.Type.Name
property) if the field does not have a name. In other words, where you currently useItem
(including those with numbers at the end, as described further below), fall back toSystem.Type.Name
. Hopefully this is easy to implement.API-wise, this can take the form of a new value
JsonUnionEncoding.NamedFieldsTypeFallback
(feel free to find a better name) to be used together withNamedFields
.Current and new behavior
Take this DU:
With the current
NamedFields
, the fields will serialize asWith the new option, it should be serialized as:
Current workarounds
To work around the lack of this feature, one has to specify field names for all fields, which IMHO is just unnecessary noise since it duplicates information:
Alternatively, one can use
JsonUnionEncoding.UnwrapRecordCases
and use a record type (either a separately defined nominal record or an anonymous record). That is even more verbose (both in the DU/record definition and when constructing).Edge cases
If multiple fields have the same type (or types with identical names), append numbers (as is currently done with
Item1
,Item2
, etc.). For example:Motivating use case
This request makes most sense in light of using single-case DU "primitives" as the field values, where the names are likely to be descriptive. I think that is a common enough pattern in F# that this feature will be useful.
I personally want this feature due to my use of JSON serialization in logging. When I log (and serialize) a domain object, they are currently full of DUs with
Item1
,Item2
, etc. Without this feature, I will either have to live with that (possibly having to open the code and see which types the values actually correspond to), or add a lot of duplication by specifying DU field names that are, for all intents and purposes, identical to the type names and thus add unnecessary noise. (Also there's the real danger of forgetting to add field names for new DUs/cases.)The text was updated successfully, but these errors were encountered: