Parsing json with PowerShell and Json.NET

I've been working lately on a set of scripts to automate setup of computers (I will write more about those later). With setup I mean from a web-developers perspective, so it includes setting up IIS, adding users to computer, changing hosts-file and changing environment-variables. An important part of this is how to specify variables to the scripts and I choose json. This short post will show you the module I implemented to parse a json file with the result of an HashTable. So let's get started. I will just show you the code and then explain it under.

function ParseItem($jsonItem) {
    if($jsonItem.Type -eq "Array") {
        return ParseJsonArray($jsonItem)
    }
    elseif($jsonItem.Type -eq "Object") {
        return ParseJsonObject($jsonItem)
    }
    else {
        return $jsonItem.ToString()
    }
}

function ParseJsonObject($jsonObj) {
    $result = @{}
    $jsonObj.Keys | ForEach-Object {
        $key = $_
        $item = $jsonObj[$key]
        $parsedItem = ParseItem $item
        $result.Add($key,$parsedItem)
    }
    return $result
}

function ParseJsonArray($jsonArray) {
    $result = @()
    $jsonArray | ForEach-Object {
        $parsedItem = ParseItem $_
        $result += $parsedItem
    }
    return $result
}

function ParseJsonString($json) {
    $config = [Newtonsoft.Json.Linq.JObject]::Parse($json)
    return ParseJsonObject($config)
}

function ParseJsonFile($fileName) {
    $json = (Get-Content $FileName | Out-String)
    return ParseJsonString $json
}

Export-ModuleMember ParseJsonFile, ParseJsonString

So let's start from the bottom of the file. I export the module members ParseJSonFile and ParseJsonString, those are the entry points for the script. ParseJsonFile is a function that just reads the json from a file and then calls ParseJsonString and it is in that function the magic starts. If you look at the first row in ParseJsonString I call an external assembly, and that is the Json.NET assembly. To call it you have to load the assembly first. You could, as I have done here, use a module manifest for the module and add the assembly to the required assembly list. If you don't want to do so you could also load the assembly before exuting a method from the module using this line:

[Reflection.Assembly]::LoadFile("path-to\Newtonsoft.Json.dll”)

When I've parsed ths json string I make one assumption, and that is that what you have in the json is an object, that is the only assumption I had since it fits my requirements. The technique I use for the rest of the script is recursion, so if you have a really long json-file you might get an exception, but I don't think that would be a problem. The ParseJsonObject creates a new HashTable and then loops over all the keys in the object from Json.NET. For each value I call the most interesting function in the script, the ParseItem function. This method looks at the value and then do three different things depending on the type of the value. If it is an object it recursively calls the ParseJsonObject function, if it is an array it calls the ParseJsonArray function and if it is neither of those it takes the string value and returns the value. The last method is the ParseJsonArray and it works the same way as the ParseJsonObject excepts it's traversing an array instead of an object.

A small example of what it looks like against one json-file I have: