Asked  7 Months ago    Answers:  5   Viewed   29 times

Why do I get unexpected ConvertTo-Json results, why do I get values like System.Collections.Hashtable and/or why does a round-trip ($Json | ConvertFrom-Json | ConvertTo-Json) fail?

Meta issue

Stackoverflow has a good mechanism to prevent duplicate questions but as far as I can see there is no mechanism to prevent questions that have a duplicate cause. Take this question as a an example: almost every week a new question comes in with the same cause, yet it is often difficult to define it as a duplicate because the question itself is just a slightly different. Nevertheless, I wouldn't be surprised if this question/answer itself ends up as a duplicate (or off-topic) but unfortunately stackoverflow has no possibility to write an article to prevent other programmers from continuing writing questions caused by this “known” pitfall.


A few examples of similar questions with the same common cause:

  • PowerShell ConvertTo-Json does not convert Array as expected (yesterday)
  • Powershell ConvertTo-json with embedded hashtable
  • powershell “ConvertTo-Json” has messed json format output
  • Nested arrays and ConvertTo-Json
  • Powershell ConvertTo-JSON missing nested level
  • How to save a JSON object to a file using Powershell?
  • Cannot convert PSCustomObjects within array back to JSON correctly
  • ConvertTo-Json flattens arrays over 3 levels deep
  • Add an array of objects to a PSObject at once
  • Why does ConvertTo-Json drop values
  • How to round-trip this JSON to PSObject and back in Powershell


So, were does this “self-answered” question differ from the above duplicates?
It has the common cause in the title and with that it might better prevent repeating questions due to the same cause.




ConvertTo-Json has a -Depth parameter:

Specifies how many levels of contained objects are included in the JSON representation.
The default value is 2.


To do a full round-trip with a JSON file you need to increase the -Depth for the ConvertTo-Json cmdlet:

$Json | ConvertFrom-Json | ConvertTo-Json -Depth 9


Probably because ConvertTo-Json terminates branches that are deeper than the default -Depth (2) with a (.Net) full type name, programmers assume a bug or a cmdlet limitation and do not read the help or about.
Personally, I think a string with a simple ellipsis (three dots: …) at the end of the cut off branch, would have a clearer meaning (see also: Github issue: 8381)


This issue often ends up in another discussion as well: Why is the depth limited at all?

Some objects have circular references, meaning that a child object could refer to a parent (or one of its grandparents) causing a infinitive loop if it would be serialized to JSON.

Take for example the following hash table with a parent property that refers to the object itself:

$Test = @{Guid = New-Guid}
$Test.Parent = $Test

If you execute: $Test | ConvertTo-Json it will conveniently stop at a depth level of 2 by default:

    "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
    "Parent":  {
                   "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
                   "Parent":  {
                                  "Guid":  "a274d017-5188-4d91-b960-023c06159dcc",
                                  "Parent":  "System.Collections.Hashtable"

This is why it is not a good idea to automatically set the -Depth to a large amount.

Tuesday, June 1, 2021
answered 7 Months ago
function ConvertTo-Json20([object] $item){
    add-type -assembly system.web.extensions
    $ps_js=new-object system.web.script.serialization.javascriptSerializer
    return $ps_js.Serialize($item)

function ConvertFrom-Json20([object] $item){ 
    add-type -assembly system.web.extensions
    $ps_js=new-object system.web.script.serialization.javascriptSerializer

    #The comma operator is the array construction operator in PowerShell
    return ,$ps_js.DeserializeObject($item)

If you're getting the error:

Add-Type : Could not load file or assembly 'System.Web.Extensions, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35 or one of its dependencies. The system cannot find the file specified. "

... these registry commands can be run (more details):

   reg add hklmsoftwaremicrosoft.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1 /f
   reg add hklmsoftwarewow6432nodemicrosoft.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1 /f
Wednesday, June 9, 2021
answered 6 Months ago

From Get-Help ConvertTo-JSON:

-Depth <Int32>
Specifies how many levels of contained objects are included in the JSON representation. The default value is 2.

Set your -Depth parameter whatever depth you need to preserve your data.

Saturday, June 19, 2021
answered 6 Months ago

I decided to not use Unscape, instead replace the unicode uxxxx characters with their string values and now it works properly:

$fileContent = 
    "something":  "http://domain/?x=1&y=2",
    "pattern":  "^(?!(\`|\~|\!|\@|\#|\$|\||\\|\'|\")).*"

$fileContent | ConvertFrom-Json | ConvertTo-Json | %{
        "\u(?<Value>[a-zA-Z0-9]{4})", {
            param($m) ([char]([int]::Parse($m.Groups['Value'].Value,
                [System.Globalization.NumberStyles]::HexNumber))).ToString() } )}

Which generates the expected output:

    "something":  "http://domain/?x=1&y=\2",
    "pattern":  "^(?!(\|\~|\!|\@|\#|\$|\||\\|\'|\")).*"
Wednesday, August 11, 2021
Michael Emerson
answered 4 Months ago

You haven't described any edge cases, but you can get the basic behavior you're looking for with something like:

import Data.List (inits, tails)

combine :: [a] -> [a] -> [[a]]
combine xs ys = zipWith3 go xs (tails ys) (inits ys)
    go a (_:xs) ys = a:ys ++ xs
    go _ _ _ = []

The key is that tails returns successive suffixes of its list starting with the full list, and inits returns successive prefixes starting with the empty list.

Thursday, September 2, 2021
Lukas Eder
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :