How to sort an object by keys using powershell - json

I have the following json file and I want it sorted by the keys/names. But so far I have been unable to figure out how to actually sort the json object by it's key/name.
Origional Settings.json
{
"files.trimTrailingWhitespace": true,
"workbench.startupEditor": "newUntitledFile",
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.detectIndentation": false,
"editor.trimAutoWhitespace": true
}
Code:
# Get Json File
$JsonFile = 'C:\Settings.json'
# Convert from Json File to Json Object
$Json = Get-Content $JsonFile | Out-String | ConvertFrom-Json
# Sort Json Object (Does Not Work!!!)
$Json = $Json | Sort-Object -Property Name
#Convert Json Object to Json File
$Json | ConvertTo-Json -depth 100 | Set-Content $JsonFile
New Settings.Json
{
"editor.detectIndentation": false,
"editor.formatOnSave": true,
"editor.tabSize": 4,
"editor.trimAutoWhitespace": true
"files.trimTrailingWhitespace": true,
"workbench.startupEditor": "newUntitledFile"
}

$json | Select-Object ($json | Get-Member -MemberType NoteProperty).Name | ConvertTo-Json

Answer was here: Powershell sort PSObject alphabetically
This issue was that the json file did not have a collection to sort but was a single object whose properties I wanted to sort. Below is the code that works.
# Build an ordered hashtable of the property-value pairs.
$SortedByProperties = [ordered] #{}
Get-Member -Type NoteProperty -InputObject $Json | Sort-Object Name |
ForEach-Object { $SortedByProperties[$_.Name] = $Json.$($_.Name) }
# Create a new object that receives the sorted properties.
$JsonFileSorted = New-Object PSCustomObject
Add-Member -InputObject $JsonFileSorted -NotePropertyMembers $SortedByProperties
$JsonFileSorted | ConvertTo-Json -depth 100 | Set-Content $JsonFile

Related

Access Object From JSON File in Powershell

I have a JSON file that I am reading in Powershell. The structure of the file is below.
[
["computer1", ["program1", versionX]],
["computer2", ["program2", versionY]],
["computer3", ["program3", "versionX"],
["program1", "versionZ"]
],
]
What I want in the program is use $env:computername and compare it with the computerX in the JSON file. If found a match, then iterate through and get the values of programName and ProgramVersion.
However, I don't know how to search through the objects and find ALL items under that.
This is what I have so far.
$rawData = Get-Content -Raw -Path "file.json" | ConvertFrom-Json
$computername=$env:computername
$data = $rawData -match $computername
This gives me objects under it. But how do I iterate through and get individual values?
But don't know what I do after that.
To start you need to be using a valid JSON file
{
"computer1": {
"program1": "versionX"
},
"computer2": {
"program2": "versionY"
},
"computer3": {
"program3": "versionX",
"program1": "versionZ"
}
}
Then you can access the PSObject Properties
$rawData = Get-Content -Raw -Path "file.json" | ConvertFrom-Json
$rawData.PsObject.Properties |
Select-Object -ExpandProperty Name |
ForEach-Object { IF ($_ -eq $env:COMPUTERNAME) {
Write-Host "Computer Name : " $_
Write-Host "Value : " $rawData."$_"
}
}
EDIT for Computer, Program, and Version as separate values
psobject.Properties.Name will give all the program names.
psobject.Properties.Name[0] will give the first program name.
psobject.Properties.value[0] will give the first program version value.
You need to increment the value to get second value, you can also use -1 as a shortcut for the last value.
$rawData = Get-Content -Raw -Path "file.json" | ConvertFrom-Json
$rawData.PsObject.Properties |
Select-Object -ExpandProperty Name |
ForEach-Object { IF ($_ -eq $env:COMPUTERNAME) {
$Computer = $_
$Values = $rawData.$_
}
}
$Computer
$Values.psobject.Properties
$Values.psobject.Properties.Name
$Values.psobject.Properties.Name[0]
$Values.psobject.Properties.value[0]
$Values.psobject.Properties.Name[1]
$Values.psobject.Properties.value[1]
You could also use the program name
$Values.program1
$Values.program2
$Values.program3

Add new key value pair to JSON file in powershell.

I have an existing JSON file with the following:
{
"buildDate": "2017-08-16",
"version": "v1.2.0"
}
How do you add new key-value pairs to an existing JSON file? For example, I would like to take the above JSON, and end up with this:
{
"buildDate": "2017-08-16",
"version": "v1.2.0",
"newKey1": "newValue1",
"newKey2": "newValue2"
}
I currently write to JSON with the following code:
#{buildDate="2017-08-16"; version="v1.2.0"} | ConvertTo-Json | Out-File .\data.json
Convert the JSON data to a PowerShell object, add the new properties, then convert the object back to JSON:
$jsonfile = 'C:\path\to\your.json'
$json = Get-Content $jsonfile | Out-String | ConvertFrom-Json
$json | Add-Member -Type NoteProperty -Name 'newKey1' -Value 'newValue1'
$json | Add-Member -Type NoteProperty -Name 'newKey2' -Value 'newValue2'
$json | ConvertTo-Json | Set-Content $jsonfile

Powershell sort PSObject alphabetically

Given a custom powershell object (bar) that is created from json (foo.json)
How would you sort the object alphabetically by key?
foo.json
{
"bbb": {"zebras": "fast"},
"ccc": {},
"aaa": {"apples": "good"}
}
Desired output
foo.json
{
"aaa": {"apples": "good"},
"bbb": {"zebras": "fast"},
"ccc": {}
}
Example
$bar = get-content -raw foo.json | ConvertFrom-Json
$bar.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False PSCustomObject System.Object
I've tried the following using sort-object
$bar = $bar | Sort
$bar = $bar | Sort-Object
Sort-Object -InputObject $bar
Sort-Object -InputObject $bar -Property Name
Sort-Object -InputObject $bar -Property #{Expression="Name"}
Sort-Object -InputObject $bar -Property #{Expression={$_.PSObject.Properties.Name}}
I've also tried converting the PSObject to a hashtable (hashtables appear to automatically sort based on name), then convert that hashtable back to json, but it looses the order again.
$buzz = #{}
$bar.psobject.properties |Foreach { $buzz[$_.Name] = $_.Value }
ConvertTo-Json $buzz -Depth 9
Update
Changed foo.json to include values aswell as keys
As Mathias R. Jessen notes, there is no collection to sort here, just a single object whose properties you want to sort, so you need reflection via Get-Member to obtain the object's properties:
$bar = get-content -raw foo.json | ConvertFrom-Json
# Build an ordered hashtable of the property-value pairs.
$sortedProps = [ordered] #{}
Get-Member -Type NoteProperty -InputObject $bar | Sort-Object Name |
% { $sortedProps[$_.Name] = $bar.$($_.Name) }
# Create a new object that receives the sorted properties.
$barWithSortedProperties = New-Object PSCustomObject
Add-Member -InputObject $barWithSortedProperties -NotePropertyMembers $sortedProps
A more streamlined version that uses -pv (-PipelineVariable) to "cache" the unsorted custom object produced by ConvertFrom-Json:
$barSortedProps = New-Object PSCustomObject
Get-Content -Raw foo.json | ConvertFrom-Json -pv jo |
Get-Member -Type NoteProperty | Sort-Object Name | % {
Add-Member -InputObject $barSortedProps -Type NoteProperty `
-Name $_.Name -Value $jo.$($_.Name)
}
what about this:
Function Sort-PSObject {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true)]$inputString
)
process {
($inputString | out-string).trim() -split "`r`n" | sort
}
}
Can send direct from pipeline
A combined version of #mklement0 and #EricWeintraub's answers:
Function Sort-PSObjectMembers {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true)]$inputObj
)
process {
$sortedProps = [ordered] #{}
Get-Member -Type NoteProperty -InputObject $inputObj | Sort-Object Name | ForEach-Object { $sortedProps[$_.Name] = $inputObj.$($_.Name) }
# Create a new object that receives the sorted properties.
$sortedObj = New-Object PSCustomObject
Add-Member -InputObject $sortedObj -NotePropertyMembers $sortedProps
return $sortedObj
}
}
So you can use it like this:
$elements | Sort-PSObjectMembers | ConvertTo-Json -Depth 32 | Out-File "elements.json" -Encoding utf8

Looping through JSON file with PowerShell

How do I loop through all items in a JSON file? The current code just writes all names on one big line:
Get-Content -Raw -Path c:\temp\Environments.Generic.json | ConvertFrom-Json | ForEach-Object {
Write-Host $_.Name
}
json file:
[
{
"Name":"EnableRetry",
"Description":"Enable retry for Webservice Task",
"Type":"Boolean",
"Sensitive":false,
"Value":true
},
{
"Name":"FolderStageFiles",
"Description":"Location of stage files",
"Type":"String",
"Sensitive":false,
"Value":"d:\\sources\\"
},
{
"Name":"FtpPassword",
"Description":"Secret FTP password",
"Type":"String",
"Sensitive":true,
"Value":"Welcome1"
}
]
I ended up Select-Object and a ForEach-Object:
$JSON = Get-Content -Raw -Path c:\temp\Environments.Generic.json | ConvertFrom-Json
$JSON | Select-Object -Property Name,Description,Type,Sensitive,Value | ForEach-Object {
Write-Host $_.Name $_.Value
}
If you do this:
$JSON = Get-Content -Raw -Path c:\temp\Environments.Generic.json | ConvertFrom-Json
$JSON will be a PowerShell object that contains a collection of objects with all of the properties defined in your JSON file.
Enter $JSON at the console and you'll see the contents of this object.
To access specific properties for specific items in the collection you could do (for example):
$JSON | Where {$_.Name -eq 'FolderStageFiles'} | Select -ExpandProperty Value

ConvertFrom-JSON to object

It looks like the way I am expecting this to work doesn't. I want multiple objects returned, but it seems to be returning just one. It is beyond me how I do it.
A very simple JSON file:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "sa01"
},
"virtualNetworkName": {
"value": "nvn01"
}
}
}
I want to dynamically add the parameters and their values into a nice pscustomobject (that would look like the following with the above data):
ParameterName | Value
===========================
storageAccountName | sa01
virtualNetworkName | nvn01
What I don't understand is why the following returns one object:
$TemplateParametersFile = "C:\Temp\deploy-Project-Platform.parameters.json"
$content = Get-Content $TemplateParametersFile -Raw
$JsonParameters = ConvertFrom-Json -InputObject $content
$JsonParameters.parameters | Measure-Object
Whilst writing this, I eventually found a solution that get's what I want, which I'll post in the answer section. Feel free to school me and improve...
I would do things a little differently, skipping the hashtable, and using the hidden PSObject property. So, picking up after you have the JSON data stored in $content, I would do something like this:
#Convert JSON file to an object
$JsonParameters = ConvertFrom-Json -InputObject $content
#Create new PSObject with no properties
$oData = New-Object PSObject
#Loop through properties of the $JsonParameters.parameters object, and add them to the new blank object
$JsonParameters.parameters.psobject.Properties.Name |
ForEach{
Add-Member -InputObject $oData -NotePropertyName $_ -NotePropertyValue $JsonParameters.parameters.$_.Value
}
$oData
By the way, it had issues converting the JSON you posted, I had to add quotes around the two values, such as "value": "sa01".
Using the same JSON file as shown above:
<#
# Read in JSON from file on disk
$TemplateParametersFile = "C:\Temp\deploy-Project-Platform.parameters.json"
$content = Get-Content $TemplateParametersFile -Raw
#>
#Retrieve JSON file from Azure storage account.
$TemplateParametersFile = "https://{storageAccountName}.blob.core.windows.net/{SomeContainer}/deploy-Project-Platform.parameters.json"
$oWc = New-Object System.Net.WebClient
$webpage = $oWc.DownloadData($TemplateParametersFile)
$content = [System.Text.Encoding]::ASCII.GetString($webpage)
#Convert JSON file to an object (IMHO- Sort of!)
$JsonParameters = ConvertFrom-Json -InputObject $content
#Build hashtable - easier to add new items - the whole purpose of this script
$oDataHash = #{}
$JsonParameters.parameters | Get-Member -MemberType NoteProperty | ForEach-Object{
$oDataHash += #{
$_.name = $JsonParameters.parameters."$($_.name)" | Select -ExpandProperty Value
}
}
#Example: adding a single item to the hashtable
$oDataHash.Add("VirtualMachineName","aDemoAdd")
#Convert hashtable to pscustomobject
$oData = New-Object -TypeName PSCustomObject
$oData | Add-Member -MemberType ScriptMethod -Name AddNote -Value {
Add-Member -InputObject $this -MemberType NoteProperty -Name $args[0] -Value $args[1]
}
$oDataHash.Keys | Sort-Object | ForEach-Object{
$oData.AddNote($_,$oDataHash.$_)
}
$oData
And the result:
storageAccountName VirtualMachineName virtualNetworkName
------------------ ------------------ ------------------
sa01 aDemoAdd nvn01
Agreed, the question asked for a Parameter / Value pair, and this results in the parameter's name being assigned as the noteproperty, but I think it will be easier to use it this way. Of course, $oDataHash returns it as a Key/value pair.
This script also pulls the JSON file directly from an Azure storage account. No need to save to disk. If you want to save to disk, change $oWc.DownloadData() to $oWc.DownloadFile() . The commented bit at the top, reads from disk.
I am sure there are much more succinct ways to achieve the same result, and I'd love to here them. For me, at the moment this works.