Skip to content

Conversation

@jborean93
Copy link
Collaborator

@jborean93 jborean93 commented Jan 5, 2024

PR Summary

Remove special handling from Windows PowerShell that treated enum values backed by Int64 and UInt64 as a string. This ensures that all enum types are serialized as a number and are only treated as a string when -EnumsAsStrings is specified.

As requested this is controlled by the new experimental feature PSSerializeJSONLongEnumAsNumber.

PR Context

When using ConvertTo-Json with an Int64 or UInt64 enum type the value is converted to a string.

enum LongEnum : long {
    LongValue = 1
}

ConvertTo-Json @{key = [LongEnum]::LongValue}

The current behaviour is:

{
    "key": "LongValue"
}

The behaviour after this PR is:

{
    "key": 1
}

Instead this PR removes the extra code that does this as it seems to be from Windows PowerShell days. Even then WinPS can deserialize an Int64 or UInt64 value just fine. This is technically a breaking change but I find the current default is surprising and honestly probably very rare to happen in the real world.

If the user wants to preserve the old behaviour they can set -EnumsAsStrings to have the enum be converted to a string like before.

PR Checklist

@pull-request-quantifier-deprecated

Image

This PR has 40 quantified lines of changes. In general, a change size of upto 200 lines is ideal for the best PR experience!


Quantification details

Label      : Extra Small
Size       : +14 -26
Percentile : 16%

Total files changed: 3

Change summary by file extension:
.cs : +1 -7
.ps1 : +13 -19

Change counts above are quantified counts, based on the PullRequestQuantifier customizations.

Why proper sizing of changes matters

Optimal pull request sizes drive a better predictable PR flow as they strike a
balance between between PR complexity and PR review overhead. PRs within the
optimal size (typical small, or medium sized PRs) mean:

  • Fast and predictable releases to production:
    • Optimal size changes are more likely to be reviewed faster with fewer
      iterations.
    • Similarity in low PR complexity drives similar review times.
  • Review quality is likely higher as complexity is lower:
    • Bugs are more likely to be detected.
    • Code inconsistencies are more likely to be detected.
  • Knowledge sharing is improved within the participants:
    • Small portions can be assimilated better.
  • Better engineering practices are exercised:
    • Solving big problems by dividing them in well contained, smaller problems.
    • Exercising separation of concerns within the code changes.

What can I do to optimize my changes

  • Use the PullRequestQuantifier to quantify your PR accurately
    • Create a context profile for your repo using the context generator
    • Exclude files that are not necessary to be reviewed or do not increase the review complexity. Example: Autogenerated code, docs, project IDE setting files, binaries, etc. Check out the Excluded section from your prquantifier.yaml context profile.
    • Understand your typical change complexity, drive towards the desired complexity by adjusting the label mapping in your prquantifier.yaml context profile.
    • Only use the labels that matter to you, see context specification to customize your prquantifier.yaml context profile.
  • Change your engineering behaviors
    • For PRs that fall outside of the desired spectrum, review the details and check if:
      • Your PR could be split in smaller, self-contained PRs instead
      • Your PR only solves one particular issue. (For example, don't refactor and code new features in the same PR).

How to interpret the change counts in git diff output

  • One line was added: +1 -0
  • One line was deleted: +0 -1
  • One line was modified: +1 -1 (git diff doesn't know about modified, it will
    interpret that line like one addition plus one deletion)
  • Change percentiles: Change characteristics (addition, deletion, modification)
    of this PR in relation to all other PRs within the repository.


Was this comment helpful? 👍  :ok_hand:  :thumbsdown: (Email)
Customize PullRequestQuantifier for this repository.

@iSazonov
Copy link
Collaborator

iSazonov commented Jan 5, 2024

I guess it was a workaround exclusively for JS.
The same case dotnet/aspnetcore#52393

I think we should preserve the workaround.

@jborean93
Copy link
Collaborator Author

jborean93 commented Jan 5, 2024

I don't see why we should cater to one language when PowerShell is able to deserialize such numbers. The fact that enums already have a workaround with -EnumAsStrings to produce the original output that is done today means people can still get the old behaviour.

Plus that exact scenario in your link serializes just fine with newtonsoft and we don't even do it for non enum long types.

PS /home/jborean> ConvertTo-Json @{foo = ([uint64]'2121241830635120343')}
{
  "foo": 2121241830635120343
}

@iSazonov
Copy link
Collaborator

iSazonov commented Jan 5, 2024

I don't see why we should cater to one language ...

Me too :-) However, a comment in the code points to an internal case. Only MSFT team can find the history and evaluate the relevance of this old workaround.
/cc @StevenBucher98 @SteveL-MSFT

@jborean93
Copy link
Collaborator Author

Just to add more ammunition to the change;

  • I’ve never actually seen an enum based on long in the wild before
  • We already serialize raw int64 and uint64 values as numbers and not strings so why should these enums be different

@iSazonov iSazonov added Review - Committee The PR/Issue needs a review from the PowerShell Committee CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log Breaking-Change breaking change that may affect users CL-BreakingChange Indicates that a PR should be marked as a breaking change in the Change Log labels Jan 5, 2024
@jborean93
Copy link
Collaborator Author

Looks like that ASP issue is specific to ASP, serializing a UInt64 works just fine with both Newtonsoft.Json and System.Text.Json

$sb = [System.Text.StringBuilder]::new()
[Newtonsoft.Json.JsonSerializer]::Create().Serialize([System.IO.StringWriter]::new($sb), @{foo=[uint64]'18446744073709551615'})
$sb.ToString()
# {"foo":18446744073709551615}

[System.Text.Json.JsonSerializer]::Serialize[hashtable](@{foo = [uint64]'18446744073709551615'})
# {"foo":18446744073709551615}

Python is also fine

import json
json.dumps({"foo": 18446744073709551615})
'{"foo": 18446744073709551615}'

@SteveL-MSFT SteveL-MSFT added WG-Cmdlets general cmdlet issues WG-NeedsReview Needs a review by the labeled Working Group and removed Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Jan 30, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Feb 6, 2024
@JamesWTruher
Copy link
Collaborator

Just to add more ammunition to the change;

* I’ve never actually seen an enum based on long in the wild before

* We already serialize raw int64 and uint64 values as numbers and not strings so why should these enums be different

PowerShell actually as a few enums based on long (or ulong)

@SteveL-MSFT
Copy link
Member

The @PowerShell/wg-powershell-cmdlets reviewed this. We believe this is a good change for modern JSON. Although historically, javascript had limits of the precision of large integers due to storing numbers as double precision floats, it seems that NodeJS v10.4.0+ now has a BigInt type and also Jordan's validation that NewtonSoft, System.Text.Json, and Python all handle big ints just fine indicate that this legacy code is no longer valid. We still have a concern about how likely this will break users so we recommend putting this behind an experimental feature to also help advertise this change. We will also recommend adding telemetry to indicate how often users are hitting this code path to ensure we believe this can be non-experimental. This can be added after this PR is merged.

@SteveL-MSFT SteveL-MSFT added WG-Reviewed A Working Group has reviewed this and made a recommendation and removed WG-NeedsReview Needs a review by the labeled Working Group labels Feb 7, 2024
Copy link
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

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

Please put behind an experimental feature

@microsoft-github-policy-service microsoft-github-policy-service bot added Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept and removed Review - Needed The PR is being reviewed labels Feb 7, 2024
@jborean93
Copy link
Collaborator Author

jborean93 commented Feb 7, 2024

PowerShell actually as a few enums based on long (or ulong)

There are 1-3 in PowerShell itself (S.M.A/M.M.I) depending on your definitions

[System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() | Where-Object {
    $_.IsSubclassOf([Enum]) -and
    $_.IsVisible -and
    [Enum]::GetUnderlyingType($_) -in @([int64], [uint64])
} | Select-Object FullName, @{
    N='Assembly'
    E={$_.Assembly.GetName().Name}
}, @{
    N='SubType'
    E={[Enum]::GetUnderlyingType($_).Name}
}
FullName                                                      Assembly                            SubType
--------                                                      --------                            -------
System.Diagnostics.Tracing.EventKeywords                      System.Private.CoreLib              Int64
System.Management.Automation.Tracing.PowerShellTraceKeywords  System.Management.Automation        UInt64
Microsoft.Management.Infrastructure.CimFlags                  Microsoft.Management.Infrastructure Int64
Microsoft.Management.Infrastructure.Options.CimOperationFlags Microsoft.Management.Infrastructure Int64
System.Net.Sockets.IOControlCode                              System.Net.Sockets                  Int64

We still have a concern about how likely this will break users so we recommend putting this behind an experimental feature to also help advertise this change.

I do personally disagree that we really do need to put it behind an experimental feature but I suppose it's a stepping stone to just having it be done by default.

Please put behind an experimental feature

Any docs on what exactly needs to be done for devs on this?

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Feb 7, 2024
@kilasuit
Copy link
Collaborator

@jborean93 the about_experimental_features doc shows what needs to be done to add experimental features in code - hope that helps

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Review - Needed The PR is being reviewed label May 15, 2024
@SteveL-MSFT SteveL-MSFT reopened this May 15, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label May 23, 2024
Remove special handling from Windows PowerShell that treated enum values
backed by Int64 and UInt64 as a string. This ensures that all enum types
are serialized as a number and are only treated as a string when
-EnumsAsStrings is specified.
@iSazonov
Copy link
Collaborator

iSazonov commented Sep 2, 2024

@SteveL-MSFT Can we merge?

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Review - Needed The PR is being reviewed label Sep 2, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Sep 9, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot removed the Review - Needed The PR is being reviewed label Sep 19, 2024
@daxian-dbw daxian-dbw enabled auto-merge (squash) September 19, 2024 16:45
@daxian-dbw daxian-dbw merged commit 3e3d83c into PowerShell:master Sep 19, 2024
@jborean93 jborean93 deleted the json-large-enum branch September 19, 2024 18:04
adityapatwardhan pushed a commit to adityapatwardhan/PowerShell that referenced this pull request Sep 19, 2024
@jshigetomi jshigetomi mentioned this pull request Dec 12, 2024
21 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BackPort-7.5.x-Done Breaking-Change breaking change that may affect users CL-BreakingChange Indicates that a PR should be marked as a breaking change in the Change Log CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log Extra Small WG-Cmdlets general cmdlet issues WG-Reviewed A Working Group has reviewed this and made a recommendation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants