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

Breaking Change: JSONC no longer works after 7.4.0 #20782

Open
5 tasks done
kyle-ntx opened this issue Nov 27, 2023 · 17 comments · May be fixed by #23817
Open
5 tasks done

Breaking Change: JSONC no longer works after 7.4.0 #20782

kyle-ntx opened this issue Nov 27, 2023 · 17 comments · May be fixed by #23817
Labels
In-PR Indicates that a PR is out for the issue Issue-Enhancement the issue is more of a feature request than a bug Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Core cmdlets in the Microsoft.PowerShell.Core module WG-Reviewed A Working Group has reviewed this and made a recommendation

Comments

@kyle-ntx
Copy link

Prerequisites

Steps to reproduce

Include a comment in json, one example can be seen here:

'{
    // A Json comment
    "string": "test"
}' | Test-Json

In previous versions of PS this worked as expected.

Expected behavior

PS> '{
    // A Json comment
    "string": "test"
}' | Test-Json
True

Actual behavior

PS> '{
    // A Json comment
    "string": "test"
}' | Test-Json

Test-Json: Cannot parse the JSON

Error details

Exception             : 
    Type           : System.Exception
    Message        : Cannot parse the JSON.
    InnerException : 
        Type               : System.Text.Json.JsonReaderException
        LineNumber         : 1
        BytePositionInLine : 4
        Message            : '/' is invalid after a value. Expected either ',', '}', or ']'. LineNumber: 1 | BytePositionInLine: 4.
        TargetSite         : 
            Name          : ThrowJsonReaderException
            DeclaringType : System.Text.Json.ThrowHelper, System.Text.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
            MemberType    : Method
            Module        : System.Text.Json.dll
        Source             : System.Text.Json
        HResult            : -2146233088
        StackTrace         : 
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
   at System.Text.Json.Utf8JsonReader.ConsumeNextToken(Byte marker)
   at System.Text.Json.Utf8JsonReader.ConsumeNextTokenOrRollback(Byte marker)
   at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
   at System.Text.Json.Utf8JsonReader.Read()
   at System.Text.Json.JsonDocument.Parse(ReadOnlySpan`1 utf8JsonSpan, JsonReaderOptions readerOptions, MetadataDb& database, StackRowStack& stack)
   at System.Text.Json.JsonDocument.ParseUnrented(ReadOnlyMemory`1 utf8Json, JsonReaderOptions readerOptions, JsonTokenType tokenType)
   at System.Text.Json.JsonDocument.ParseValue(ReadOnlyMemory`1 json, JsonDocumentOptions options)
   at System.Text.Json.Nodes.JsonNode.Parse(String json, Nullable`1 nodeOptions, JsonDocumentOptions documentOptions)
   at Microsoft.PowerShell.Commands.TestJsonCommand.ProcessRecord()
    HResult        : -2146233088
TargetObject          : {
    // A Json comment
    "string": "test"
}
CategoryInfo          : InvalidData: ({
    // A Json com… "string": "test"
}:String) [Test-Json], Exception
FullyQualifiedErrorId : InvalidJson,Microsoft.PowerShell.Commands.TestJsonCommand
InvocationInfo        : 
    MyCommand        : Test-Json
    ScriptLineNumber : 4
    OffsetInLine     : 6
    HistoryId        : 4
    Line             : }' | Test-Json
    Statement        : Test-Json
    PositionMessage  : At line:4 char:6
                       + }' | Test-Json
                       +      ~~~~~~~~~
    InvocationName   : Test-Json
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1
PipelineIterationInfo :

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

image
image

@kyle-ntx kyle-ntx added the Needs-Triage The issue is new and needs to be triaged by a work group. label Nov 27, 2023
@kyle-ntx
Copy link
Author

It's likely the switch to System.Text.Json that has caused this: #18141

@rhubarb-geek-nz
Copy link

Include a comment in json, one example can be seen here:

'{
    // A Json comment
    "string": "test"
}' | Test-Json

Unlike XML, JSON does not have a representation for comments, see The JavaScript Object Notation (JSON) Data Interchange Format.

What you have there is some JSON with a C++ style comment which is not part of the specification.

There are hundreds of JSON parsers and the only thing they have in common is the JSON specification. When you exchange JSON between different systems you risk parser failures if you don't stick to the spec.

@Nikolai48Nick
Copy link

README

@mklement0
Copy link
Contributor

mklement0 commented Nov 27, 2023

@kyle-ntx, this selective switch to the System.Text.Json APIs is very unfortunate, as it is now at odds with the lenient rules of the JSON.Net APIs that still underly the ConvertFrom-Json cmdlet:

Specifically, the latter supports:

  • unquoted property names
  • single-quoted property values
  • a single, extraneous trailing ,
  • C-style comments - both to-end-of-line // comments and /* ... */ block comments.

Thus, there is now a blatant discrepancy between what ConvertFrom-Json accepts in practice, and what Test-Json flags as invalid - and while their behaviors definitely should align by default, there's not even an opt-in for testing strict adherence to the JSON spec. (seemingly not offered by JSON.NET - see #10628 (comment))

# All of these *succeed* with ConvertFrom-Json, but *fail* with Test-Json
@(
  '{ foo: "bar" }'
  "{ foo: 'bar' }"
  "{ foo: 'bar', }"
  "{ foo: 
    'bar' // bar it is
  }"
  "{ foo: /* et
    tu? */
    'bar'
  }"
) | Convertfrom-Json

@rhubarb-geek-nz
Copy link

rhubarb-geek-nz commented Nov 27, 2023

While being lenient may sound helpful, in the words of The Fat Controller, it causes confusion and delay.

When passing JSON from one system to another you have no control over the other systems parser and should stick to the strict JSON specification. If you don't then you are basically passing garbage between one system and another and cannot trust any parser to interpret the data as you intended and may silently ignore some fields and accept others.

@mklement0
Copy link
Contributor

mklement0 commented Nov 27, 2023

@rhubarb-geek-nz:

  • It is indubitably desirable to have the option of performing a strict check for compliance with the JSON spec.

  • Historically, Test-Json used the same APIs as ConvertFrom-Json, and the divergence discussed here therefore caused an unacknowledged and undocumented breaking change.

  • It therefore makes sense for Test-Json and ConvertFrom-Json to continue to be aligned by default, and to offer a - yet to be implemented - opt-in to request strict compliance checking - not only via Test-Json, but also via ConvertFrom-Json.

@mklement0
Copy link
Contributor

Case in point: The Test-Json help topic still shows the following example as succeeding (which was true up to v7.3.x), which now fails:

"{'name': 'Ashley', 'age': 25}" | Test-Json

@rhubarb-geek-nz
Copy link

How to allow some kinds of invalid JSON with System.Text.Json

An example they give is

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25, // Fahrenheit 77
  "Summary": "Hot", /* Zharko */
  // Comments on
  /* separate lines */
}

So worth running the current failures through that to find the best set of options represent the original behaviour.

@mklement0
Copy link
Contributor

@rhubarb-geek-nz, from what I can tell, the System.Text.Json APIs fundamentally disallow unquoted property names as well as single-quoted property names and values (there's nothing in System.Text.Json.JsonSerializerOptions that suggests otherwise), so at least using these APIs directly won't work for emulating what ConvertFrom-Json accepts.

@rhubarb-geek-nz
Copy link

You might have to bite the bullet and have the ConvertFrom-Json Cmdlet supporting both parsers, where a switch selects which one to use.

@kyle-ntx
Copy link
Author

kyle-ntx commented Nov 27, 2023

Unlike XML, JSON does not have a representation for comments, see The JavaScript Object Notation (JSON) Data Interchange Format.

While this is true, it does appear that JSON with Comments worked before, and does not now, and the introduction of this change has broken scripts which we have relied on for quite some time.

I do agree that supporting things that are not part of a standard is fraught with danger, however, introducing a breaking change as part of a minor update and with out warning is not common practice and, if done at all, should be done with some forewarning and in the form of a major version change.

@IISResetMe
Copy link
Collaborator

Looks like this can be fixed pretty easily, System.Text.Json allows skipping/ignoring comment literals in the input: IISResetMe@cc67613

@mklement0
Copy link
Contributor

mklement0 commented Nov 30, 2023

@IISResetMe: As noted, this isn't sufficient: what System.Text.Json fundamentally lacks support for - unless I'm missing something - are unquoted property names and single-quoted property names and string values.

That is, it won't be able to validate something "{ foo: 'bar' }", which ConvertFrom-Json happily accepts.

I think what is called for is:

  • Test-Json and ConverFrom-Json should exhibit the same, backward-compatible behavior by default.
  • Ideally, extend them to support various syntax variants on an opt-in basis, notably allowing for enforcing strict adherence to the JSON spec.

@IISResetMe
Copy link
Collaborator

Sorry for the lazy comment above @mklement0, I was solely referring to the comment parsing issue with Test-Json.

The following should evaluate without error with the suggested fix:

@'
{
  // this is a comment
  "property": "value"
}
'@ |Test-Json

... but obviously won't resolve the bareword/single-quote property name issue. Fully agree on backwards compat.

@mklement0
Copy link
Contributor

@daxian-dbw daxian-dbw added the WG-Cmdlets-Core cmdlets in the Microsoft.PowerShell.Core module label Mar 18, 2024
@SteveL-MSFT SteveL-MSFT added the WG-NeedsReview Needs a review by the labeled Working Group label Mar 18, 2024
@SteveL-MSFT
Copy link
Member

@PowerShell/wg-powershell-cmdlets reviewed this. System.Text.Json.Node allows for options that would allow for comments. We believe that the default behavior should adhere to the standards (enabling portable JSON), but have a new -Option parameter which takes an array of options (currently would support Comments and TrailingComma).

@SteveL-MSFT SteveL-MSFT added WG-Reviewed A Working Group has reviewed this and made a recommendation Issue-Enhancement the issue is more of a feature request than a bug Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors and removed WG-NeedsReview Needs a review by the labeled Working Group Needs-Triage The issue is new and needs to be triaged by a work group. labels Apr 17, 2024
@mklement0
Copy link
Contributor

mklement0 commented Apr 17, 2024

@SteveL-MSFT, the proposed fix:

  • doesn't acknowledge the fundamentally breaking change that occurred - see also: Cannot parse the JSON. eror #21338
  • even with the opt-in doesn't fully restore the previous functionality, as System.Text.Json fundamentally does not support single-quoted tokens, and also doesn't support bareword properties.

Update: Just saw the following summary:

@microsoft-github-policy-service microsoft-github-policy-service bot added the In-PR Indicates that a PR is out for the issue label May 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In-PR Indicates that a PR is out for the issue Issue-Enhancement the issue is more of a feature request than a bug Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Core cmdlets in the Microsoft.PowerShell.Core module WG-Reviewed A Working Group has reviewed this and made a recommendation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants