Skip to content

ConvertTo-Json: unexpected behavior with objects that have ETS properties (NoteProperty, ScriptProperty) #5797

@mklement0

Description

@mklement0

Instances of types that normally serialize to a JSON scalar (basic JSON data type: string, number, Boolean) should not serialize to objects just because - situationally - NoteProperty and ScriptProperty members may be present - not least because such members may be added automatically by PowerShell.

Additionally, the current behavior is inconsistent, possibly related to #5579.

Also, with a ScriptProperty member present on a [string] instance, ConvertTo-Json crashes as of PowerShell Core v6.1.0-preview.3 - see #7091

Steps to reproduce

[ordered] @{ d1 = [datetime]::now; d2 = get-date  } | ConvertTo-Json

Expected behavior

{
  "d1": "2018-01-05T09:25:37.037783+01:00",
  "d2": "2018-01-05T09:25:37.037783+01:00",
}

Actual behavior

{
  "d1": "2018-01-05T09:25:37.037783+01:00",
  "d2": {
    "value": "2018-01-05T09:25:37.037915+01:00",
    "DisplayHint": 2,
    "DateTime": "Friday, January 5, 2018 9:25:37 AM"
  }
}

Note how the presence of the DisplayHint and DateTime members added by Get-Date caused the value to no longer serialize as a single string, but as an object with said members (yet not any of the type's regular properties).

In some cases, using an intermediate variable makes the problem go away:

$dt = get-date
[ordered] @{ d1 = [datetime]::now; d2 = $dt  } | ConvertTo-Json # OK

That said, with other objects, such as those with provider-added properties, the problem surfaces even with an intermediate variable:

'hi' > t.txt
$s = Get-Content t.txt
$s | ConvertTo-Json   # !! outputs a JSON object with many provider properties

Again, what normally serializes as a string is unexpectedly serialized as an object due to the presence of NoteProperty members.

Finally, when sending an instance with even just a ScriptProperty member through the pipeline, the problem surfaces too:

> [datetime]::now | ConvertTo-Json
{
  "value": "2018-01-07T06:18:29.821274+01:00",
  "DateTime": "Sunday, January 7, 2018 6:18:29 AM"
}

Note that if the instance is nested inside a hashtable / custom object, the problem does not occur, as demonstrated above.
It also doesn't occur if you use -InputObject instead of the pipeline: ConvertTo-Json ([datetime]::now)

Workarounds

Either: apply .psobject.baseobject to the command producing the object to serialize to JSON:

@{ d = (Get-Date).psobject.BaseObject } | ConvertTo-Json

Or: cast to the expected type (or, with an intermediate variable, type-constrain it):

@{ d = [datetime] (Get-Date) } | ConvertTo-Json

Note: The above workarounds only work if the problematic object is not the input object as a whole.

If so, the only workaround I'm aware of is to pass the input object via -InputObject rather than the pipeline:

ConvertTo-Json ([datetime] (Get-Date))   # OK, due to using -InputObject

# !! BROKEN, due to using the pipeline, though, curiously, only
# !! the "DateTime" ScriptProperty shows up, not the "DisplayHint" NoteProperty
[datetime] (Get-Date) | ConvertTo-Json  

Environment data

PowerShell Core v6.0.0-rc.2 (v6.0.0-rc.2) on macOS 10.13.2

Metadata

Metadata

Assignees

Labels

Committee-ReviewedPS-Committee has reviewed this and made a decisionIssue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifResolution-FixedThe issue is fixed.WG-Cmdlets-Utilitycmdlets in the Microsoft.PowerShell.Utility module

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions