Json circular reference in powershell 2.0 with javascriptSerializer
Asked Answered
P

1

8

I´m writing a script in powershell 2.0 and an upgrade to 3.0 or higher is not possible right now. In this script I try to serialize some data to JSON with the code from this link (PowerShell 2.0 ConvertFrom-Json and ConvertTo-Json implementation):

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)
}

My problem is that I somehow get a circular reference and I really don´t know why. I set up a litte piece of test data and the structure looks in powershell like this:

$testRoot = @{
    "id" = "1"
    "children" = @( 
        @{
            "id" = "2"
            "children" = @( 
                @{
                    "id" = "2";
                };
                @{
                    "id" = "3";
                }
            );
        };
        @{
            "id" = "4"
            "children" = @( 
                @{
                    "id" = "5";
                }
            );
        }
    )
}

I know it looks junky, but I just need it in this format.

The structures I need to serialize have a few more layers, so even more "children" and there is the point where it gets strange.

When I try this:

ConvertTo-Json20 $testRoot

everything works fine. The structure gets parsed like this:

{
   "id":"1",
   "children":[
        {
            "id":"2",
            "children":[
               {
                   "id":"2"
               },
               {
                   "id":"3"
               }
            ]
        },
        {
            "id":"4",
            "children":[
               {
                   "id":"5"
               }
            ]
        }
   ]
}

But now comes the problem. As mentioned the structure has more layers, so I try this which just sets the data in an array.

ConvertTo-Json20 @($testRoot)

But it does not work I just get an error message saying:

Exception in method "Serialize" with 1 argument(s):  
"While serializing an object of type "System.Management.Automation.PSParameterizedProperty" a circular reference was discovered."
At C:\Users\a38732\Desktop\Temp.ps1:34 symbol:28
+     return $ps_js.Serialize <<<< ($item)
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

(I translated the error message from german, so there might be some words different in the english version...)

Physiological answered 26/8, 2016 at 8:29 Comment(5)
I just tried it with Powershell 4.0 on another machine and it worked with the build in json serializer. ConvertTo-Json @($testRoot)Physiological
Does somebody know how to tweek the javascriptSerializer to serialize that? The documentation does not help very much...Physiological
Your example is malformed. Entries in arrays are separated by commas, not semicolons. The function also doesn't take an array, it takes a single [object].Inclinatory
@TheIncorrigible1 - agree that array elements are normally separated by commas, but actually the @( ) array subexpression operator is special. To quote: "Returns the result of one or more statements as an array." e.g. @(Get-Date; Start-Sleep -s 1; Get-Date). And since an array is a object, the function is perfectly valid. One problem is that JavaScriptSerializer.Serialize() is useless for anything but simple JSON.Usurer
If it is just about serializing a (complex) object for reuse by the same system or exchanging with another PowerShell system, you might consider the ConvertTo-Expression cmdlet from the PowerShell Gallery which is downwards compatible with PSv2.0. The results can easily be invoked (deserialized) with Invoke-Expression or just an ampersand (&) or dot sourcing a .ps1 file containing the results.Mcclelland
U
4

One problem is the use of the JavaScriptSerializer class itself. As of this date the documentation itself concedes it should not be used to serialize nor deserialize JSON. To quote:

Json.NET should be used serialization and deserialization.

If you're able to use third-party libraries like Json.NET, here's a simple function that does what you need given the data structure in the OP:

function ConvertTo-JsonNet {
   [CmdletBinding()]
    param(
        [Parameter(Mandatory)] $object,
        [Parameter(Mandatory)] [string]$jsonNetPath,
        [switch]$indent,
        [switch]$preserveReferencesHandling
    )
    Add-Type -Path $jsonNetPath;

    $formatting = if ($indent.IsPresent) { [Newtonsoft.Json.Formatting]::Indented; }
    else { [Newtonsoft.Json.Formatting]::None; }

    $settings = New-Object Newtonsoft.Json.JsonSerializerSettings;
    if ($preserveReferencesHandling.IsPresent) { 
        $settings.PreserveReferencesHandling = [Newtonsoft.Json.PreserveReferencesHandling]::Objects;
    }

    [Newtonsoft.Json.JsonConvert]::SerializeObject($object, $formatting, $settings);
}

Simple usage, assuming Newtonsoft.Json.dll is in the same directory as your script:

$dllPath = Join-Path $PSScriptRoot 'Newtonsoft.Json.dll';
ConvertTo-JsonNet @($testRoot) $dllPath;

Output:

[{"id":"1","children":[{"id":"2","children":[{"id":"2"},{"id":"3"}]},{"id":"4","children":[{"id":"5"}]}]}]

You can manually download the .dll from the nuget package project site. It has a .nupkg file extension, but it's a zipped archive, so rename the extension to .zip and you're set. In the lib sub-directory there are .dll files for .NET versions from 2.0 through 4.5.

Usurer answered 17/7, 2018 at 23:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.