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

Added snake and kebab naming policies to JSON serializer #69613

Merged
merged 18 commits into from
Oct 20, 2022

Conversation

YohDeadfall
Copy link
Contributor

@YohDeadfall YohDeadfall commented May 20, 2022

Fixes #782

Description

As it was discussed in the issue, I moved code from https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies which implements snake and kebab policies.

Customer Impact

After a few years they finally will get these highly demanded features.

Testing

Yes, there are test checking different cases used in previous attempts too, but more of them were added.

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label May 20, 2022
@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Text.Json new-api-needs-documentation and removed community-contribution Indicates that the PR has been added by a community member labels May 20, 2022
@dotnet-issue-labeler
Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented May 20, 2022

Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
See info in area-owners.md if you want to be subscribed.

Issue Details

Fixes Issue #782

Description

As it was discussed in the issue, I moved code from https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies which implements snake and kebab policies.

Customer Impact

After a few years they finally will get these highly demanded features.

Testing

Yes, there are test checking different cases used in previous attempts too, but more of them were added.

Author: YohDeadfall
Assignees: -
Labels:

area-System.Text.Json, new-api-needs-documentation

Milestone: -

@teo-tsirpanis
Copy link
Contributor

@YohDeadfall CI fails with

##[error]src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs(1498,1): error SA1028: (NETCORE_ENGINEERING_TELEMETRY=Build) Code should not contain trailing whitespace

@YohDeadfall
Copy link
Contributor Author

Okay, it should be fine now:

  • Types are explicit everywhere as @gfoidl reminded me;
  • Bug with slicing is fixed (forgot to subtract an index while switching from indexer slicing).

@eiriktsarpalis eiriktsarpalis added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label May 21, 2022
@YohDeadfall
Copy link
Contributor Author

@teo-tsirpanis, while the public API shapes is awaiting for an approval, internals can be reviewed even now.

Copy link
Contributor

@NinoFloris NinoFloris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some feedback, the code is a bit brittle at the moment.

I rewrote WriteWord to something a bit more resilient, feel free to copy it over.
https://gist.github.com/NinoFloris/38597a90fb7c422d0244098c6fd94809

Also I noticed newlines are left alone entirely?

};

if (currentCategory == CharCategory.Lowercase && char.IsUpper(next) ||
next == '_')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this fit with custom boundary chars?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, Nino (: Thank you for the valuable review.

That's a trimmed version of the original implementation which used word boundary and uppercase-lowercase logic to get words from an input string, but it was too heavy for a name policy as @ericstj told me in the previous attempt or in the issue. Therefore, the current implementation only support CLR member names and not any input string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't recall the exact conversation but it may have been about bringing in new library dependencies to handle word boundaries? Ultimately my guidance is usually just a suggestion and not a hard requirement -- we make the hard calls in API review and ultimately in PR with the area owners. Not everything is settled and if there is new data that invalidates past decisions it can be revisited. We do prioritize the right extensibility points in JSON rather than implementing every feature. cc @dotnet/area-system-text-json

return;

int required = result.IsEmpty
? word.Length
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What works for the BCL in span.ToLower/ToUpper works here but a comment stating that we don't expect the length to change after upper/lowercasing, so we can just use length here, would be good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to avoid assumptions on how internally such things work. Any dependency can be changed, but the code must continue to work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree which is why my gist code doesn't rely on it :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can make the assumption that length can't change:

public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
{
if (source.Overlaps(destination))
throw new InvalidOperationException(SR.InvalidOperation_SpanOverlappedOperation);
// Assuming that changing case does not affect length
if (destination.Length < source.Length)
return -1;
if (GlobalizationMode.Invariant)
InvariantModeCasing.ToUpper(source, destination);
else
TextInfo.Invariant.ChangeCaseToUpper(source, destination);
return source.Length;
}

cc @GrabYourPitchforks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but if that part changes at some point for whatever reason, the converter must be fixed too. Would someone remember to do that? I guess, not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have evidence suggesting that the UTF-16 spec could permit variable character lengths when mapping casings? This would break the ToUpper/ToLower methods on char.

cc @dotnet/area-system-globalization

Copy link
Member

@tarekgh tarekgh Oct 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For ToUpper/ToLower on char, we always map it manually through caching some tables from ICU. So, we guarantee 1-to-1 mapping. For the overloads that takes string, it is possible to get variable length.

@krwq
Copy link
Member

krwq commented Sep 16, 2022

@YohDeadfall given the newer APIs haven't been reviewed yet I'll refrain from review for now and let's not go too far with implementation or testing to not do unnecessary work.

We've also discussed the new APIs during our JSON triage meeting and we don't feel strongly about either having or not having lowercase/uppercase APIs so we'll let API review folks make the final call. Specifically we don't have enough proof to believe uppercase version will be useful or not useful for anyone and at the same time the APIs seem to be isolated enough that we're ok with having them.

@eiriktsarpalis eiriktsarpalis removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Sep 20, 2022
Copy link
Contributor

@deeprobin deeprobin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your PR. It already looks very good. Have noted a few small things.

Looking forward to seeing this change with .NET 8.

@YohDeadfall
Copy link
Contributor Author

@eiriktsarpalis, I've addressed all name issues except one with which I strongly disagree (result and resultLength). In #69613 (comment) you can see the result of passing variables to static local methods instead of using a state struct which captures them. Therefore, it's ready for the final review, I guess.

@YohDeadfall YohDeadfall force-pushed the json-naming-policies branch 3 times, most recently from 1914413 to 40d652c Compare October 19, 2022 12:36
@YohDeadfall YohDeadfall requested a review from krwq October 20, 2022 09:19
currentCategoryUnicode == UnicodeCategory.UppercaseLetter &&
char.IsLower(next))
{
WriteWord(chars.Slice(first, index - first), ref result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious - should we be using identical rules to CamelCase except also adding - or _?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can do that, but knowing about "compatibility concerns" I expect to hear "no" from other reviewers (:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And actually, what you asked is how naming policies work in other projects and platforms. They always split the input and then transform each word and concatenate them back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please file an issue to consider joining this code with CamelCasing and I think we're ready to merge after that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@krwq krwq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with question

@MartyIX
Copy link
Contributor

MartyIX commented Oct 20, 2022

Any chance of this getting into .NET 7 via servicing?

@YohDeadfall
Copy link
Contributor Author

Guess, no, but feel free to use my package which source was used for that PR and later adjusted to address review issues from here:

I'll release version 1.0.0 soon.

@YohDeadfall YohDeadfall deleted the json-naming-policies branch October 20, 2022 12:38
@krwq
Copy link
Member

krwq commented Oct 20, 2022

I doubt that will meet servicing bar. System.Text.Json ships as nuget package so you should be able to consume it with the next preview or nightly.

Convert("aHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
Assert.Equal(
"a_towel_it_says_is_about_the_most_massively_useful_thing_an_interstellar_hitchhiker_can_have_partly_it_has_great_practical_value_you_can_wrap_it_around_you_for_warmth_as_you_bound_across_the_cold_moons_of_jaglan_beta_you_can_lie_on_it_on_the_brilliant_marble_sanded_beaches_of_santraginus_v_inhaling_the_heady_sea_vapors_you_can_sleep_under_it_beneath_the_stars_which_shine_so_redly_on_the_desert_world_of_kakrafoon_use_it_to_sail_a_miniraft_down_the_slow_heavy_river_moth_wet_it_for_use_in_hand_to_hand_combat_wrap_it_round_your_head_to_ward_off_noxious_fumes_or_avoid_the_gaze_of_the_ravenous_bugblatter_beast_of_traal_a_mind_bogglingly_stupid_animal_it_assumes_that_if_you_cant_see_it_it_cant_see_you_daft_as_a_brush_but_very_very_ravenous_you_can_wave_your_towel_in_emergencies_as_a_distress_signal_and_of_course_dry_yourself_of_with_it_if_it_still_seems_to_be_clean_enough",
Convert("ATowelItSaysIsAboutTheMostMassivelyUsefulThingAnInterstellarHitchhikerCanHave_PartlyItHasGreatPracticalValue_YouCanWrapItAroundYouForWarmthAsYouBoundAcrossTheColdMoonsOfJaglanBeta_YouCanLieOnItOnTheBrilliantMarbleSandedBeachesOfSantraginusVInhalingTheHeadySeaVapors_YouCanSleepUnderItBeneathTheStarsWhichShineSoRedlyOnTheDesertWorldOfKakrafoon_UseItToSailAMiniraftDownTheSlowHeavyRiverMoth_WetItForUseInHandToHandCombat_WrapItRoundYourHeadToWardOffNoxiousFumesOrAvoidTheGazeOfTheRavenousBugblatterBeastOfTraalAMindBogglinglyStupidAnimal_ItAssumesThatIfYouCantSeeItItCantSeeYouDaftAsABrushButVeryVeryRavenous_YouCanWaveYourTowelInEmergenciesAsADistressSignalAndOfCourseDryYourselfOfWithItIfItStillSeemsToBeCleanEnough"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add a few tests validating that the output matches that of the Json.NET implementation, particularly when it comes to nontrivial inputs. We have time until .NET 8 ships, so we can always introduce breaking changes if needed.

@SaphireLattice
Copy link

Is there a particular reason why JsonSeparatorNamingPolicy is an internal class that is not exposed? I figure it could be rather useful in rare odd cases? Unless there's something I misunderstand about marking things internal.

@YohDeadfall
Copy link
Contributor Author

That's pretty simple to answer. That type is an implementation detail which shall not be exposed especially in case when it's a part of the SDK where anything public is written in stone and cannot be changed. Odd scenarios aren't a thing which is covered in developer kits, platforms and runtimes.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 22, 2022
@eiriktsarpalis eiriktsarpalis added this to the 8.0.0 milestone Jan 31, 2023
@jeffhandley jeffhandley added the blog-candidate Completed PRs that are candidate topics for blog post coverage label Mar 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Text.Json blog-candidate Completed PRs that are candidate topics for blog post coverage new-api-needs-documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add snake_case support for System.Text.Json