Round-tripping problems in PowerShell ConvertTo-Json cmdlet

You might expect PowerShell ConvertTo-Json produce a JSON representation of a ‘POD’ that can be parsed (in particular, by ConvertFrom-Json) into a POD object that equals to the previously serialised one. However, this is not true for many cases, the most surprising among which are double.

Note that those objects aren’t really PODs in PowerShell (.NET), but such metaphor is appropriate.

Numerical data (double)

This is the most astonishing to me, and is the reason why I wrote this blog entry. Let’s focus on the case of doubles. You might expect ConvertTo-Json use the R (round-trip) format so that when the string is parsed, it float-point-number-equals (i.e., ==) the original value.

The sensible deduction above fails, surprisingly due to that R doesn’t really round-trip on x64. For those too lazy to click on the link, use G17 (for double) or G9 (for float). This seems to be a bug in .NET and has been fixed in a future (or current?) version of it.

Guid and Uri

They are converted into strings, and when converted back, the type information is lost.

@{ 'Uri' = [uri]::new('https://geelaw.blog/');
   'Guid' = [guid]::Parse('00000000-0000-0000-c000-000000000046')
} | ConvertTo-Json -Compress;
# {"Guid":"00000000-0000-0000-c000-000000000046","Uri":"https://geelaw.blog/"}

This is of minor problem, as people normally expect GUIDs and URIs to be serialised as strings in JSON.

Exercise. Do you feel the GUID above familiar?

DateTime and friends

The JSON cmdlets uses a special format to represent a DateTime as a string. For example,

[psobject]@{ 'value' =
    [datetime]::new(2000, 1, 1, 0, 0, 0, [System.DateTimeKind]::Utc)
} | ConvertTo-Json -Compress;
# {"value":"\/Date(946684800000)\/"}

[psobject]@{ 'value' = '/Date(946684800000)/' } | ConvertTo-Json -Compress;
# {"value":"/Date(946684800000)/"}

Note that when interpreted as plain JSON, value is the string /Date(946684800000)/. However, if you try serialise that string into JSON, ConvertTo-Json will not escape the (forward) slash. The cmdlets take advantage of this knowledge to keep type information within their little 2-cmdlet world.

Fatal is that the value itself doesn’t round-trip. The problem occurs at the point of conversion to JSON. If you try changing Utc to Local in the example above, you get the same result even if your time-zone is currently not aligned with UTC. ConvertFrom-Json always treats the string as a UTC DateTime, but ConvertTo-Json forgets to convert the value to UTC when serialising it.

Please enable JavaScript to view the comments powered by Disqus.