Add new key value pair to JSON file in powershell. - json

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

Related

powershell if statement for null condition [duplicate]

I have a piece of code that works but I want to know if there is a better way to do it. I could not find anything related so far. Here are the facts:
I have an object with n properties.
I want to convert this object to JSON using (ConvertTo-Json).
I don't want to include in the JSON those object properties that are not valued.
Building the object (not really important):
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name TableName -Value "MyTable"
Add-Member -InputObject $object -MemberType NoteProperty -Name Description -Value "Lorem ipsum dolor.."
Add-Member -InputObject $object -MemberType NoteProperty -Name AppArea -Value "UserMgmt"
Add-Member -InputObject $object -MemberType NoteProperty -Name InitialVersionCode -Value ""
The line that I need improvements (to filter out the non-valued properties and not include them in the JSON)
# So I want to 'keep' and deliver to the JSON only the properties that are valued (first 3).
$object | select -Property TableName, Description, AppArea, InitialVersion | ConvertTo-Json
What this line delivers:
Results:
{
"TableName": "MyTable",
"Description": "Lorem ipsum dolor..",
"AppArea": "UserMgmt",
"InitialVersion": null
}
What I want to obtain:
{
"TableName": "MyTable",
"Description": "Lorem ipsum dolor..",
"AppArea": "UserMgmt"
}
What I've tried and works, but I don't like it since I have much more properties to handle:
$JSON = New-Object PSObject
if ($object.TableName){
Add-Member -InputObject $JSON -MemberType NoteProperty -Name TableName -Value $object.TableName
}
if ($object.Description){
Add-Member -InputObject $JSON -MemberType NoteProperty -Name Description -Value $object.Description
}
if ($object.AppArea){
Add-Member -InputObject $JSON -MemberType NoteProperty -Name AppArea -Value $object.AppArea
}
if ($object.InitialVersionCode){
Add-Member -InputObject $JSON -MemberType NoteProperty -Name InitialVersionCode -Value $object.InitialVersionCode
}
$JSON | ConvertTo-Json
Something like this?
$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name TableName -Value "MyTable"
Add-Member -InputObject $object -MemberType NoteProperty -Name Description -Value "Lorem ipsum dolor.."
Add-Member -InputObject $object -MemberType NoteProperty -Name AppArea -Value "UserMgmt"
Add-Member -InputObject $object -MemberType NoteProperty -Name InitialVersionCode -Value ""
# Iterate over objects
$object | ForEach-Object {
# Get array of names of object properties that can be cast to boolean TRUE
# PSObject.Properties - https://msdn.microsoft.com/en-us/library/system.management.automation.psobject.properties.aspx
$NonEmptyProperties = $_.psobject.Properties | Where-Object {$_.Value} | Select-Object -ExpandProperty Name
# Convert object to JSON with only non-empty properties
$_ | Select-Object -Property $NonEmptyProperties | ConvertTo-Json
}
Result:
{
"TableName": "MyTable",
"Description": "Lorem ipsum dolor..",
"AppArea": "UserMgmt"
}
I have the following function in my profile for this purpose. Advantage: I can pipe a collection of objects to it and remove nulls from all the objects on the pipeline.
Function Remove-Null {
[cmdletbinding()]
param(
# Object to remove null values from
[parameter(ValueFromPipeline,Mandatory)]
[object[]]$InputObject,
#By default, remove empty strings (""), specify -LeaveEmptyStrings to leave them.
[switch]$LeaveEmptyStrings
)
process {
foreach ($obj in $InputObject) {
$AllProperties = $obj.psobject.properties.Name
$NonNulls = $AllProperties |
where-object {$null -ne $obj.$PSItem} |
where-object {$LeaveEmptyStrings.IsPresent -or -not [string]::IsNullOrEmpty($obj.$PSItem)}
$obj | Select-Object -Property $NonNulls
}
}
}
Some examples of usage:
$AnObject = [pscustomobject]#{
prop1="data"
prop2="moredata"
prop5=3
propblnk=""
propnll=$null
}
$AnObject | Remove-Null
prop1 prop2 prop5
----- ----- -----
data moredata 3
$ObjList =#(
[PSCustomObject]#{
notnull = "data"
more = "sure!"
done = $null
another = ""
},
[PSCustomObject]#{
notnull = "data"
more = $null
done = $false
another = $true
}
)
$objList | Remove-Null | fl #format-list because the default table is misleading
notnull : data
more : sure!
notnull : data
done : False
another : True
beatcracker's helpful answer offers an effective solution; let me complement it with a streamlined version that takes advantage of PSv4+ features:
# Sample input object
$object = [pscustomobject] #{
TableName = 'MyTable'
Description = 'Lorem ipsum dolor...'
AppArea = 'UserMgmt'
InitialVersionCode = $null
}
# Start with the list of candidate properties.
# For simplicity we target *all* properties of input object $obj
# but you could start with an explicit list as wellL
# $candidateProps = 'TableName', 'Description', 'AppArea', 'InitialVersionCode'
$candidateProps = $object.psobject.properties.Name
# Create the filtered list of those properties whose value is non-$null
# The .Where() method is a PSv4+ feature.
$nonNullProps = $candidateProps.Where({ $null -ne $object.$_ })
# Extract the list of non-null properties directly from the input object
# and convert to JSON.
$object | Select-Object $nonNullProps | ConvertTo-Json
I made my own modified version of batmanama's answer that accepts an additional parameter, letting you remove elements that are also present in the list present in that parameter.
For example:
Get-CimInstance -ClassName Win32_UserProfile |
Remove-Null -AlsoRemove 'Win32_FolderRedirectionHealth' | Format-Table
I've posted a gist version including PowerShell documentation as well.
Function Remove-Null {
[CmdletBinding()]
Param(
# Object from which to remove the null values.
[Parameter(ValueFromPipeline,Mandatory)]
$InputObject,
# Instead of also removing values that are empty strings, include them
# in the output.
[Switch]$LeaveEmptyStrings,
# Additional entries to remove, which are either present in the
# properties list as an object or as a string representation of the
# object.
# I.e. $item.ToString().
[Object[]]$AlsoRemove = #()
)
Process {
# Iterate InputObject in case input was passed as an array
ForEach ($obj in $InputObject) {
$obj | Select-Object -Property (
$obj.PSObject.Properties.Name | Where-Object {
-not (
# If prop is null, remove it
$null -eq $obj.$_ -or
# If -LeaveEmptyStrings is not specified and the property
# is an empty string, remove it
(-not $LeaveEmptyStrings.IsPresent -and
[string]::IsNullOrEmpty($obj.$_)) -or
# If AlsoRemove contains the property, remove it
$AlsoRemove.Contains($obj.$_) -or
# If AlsoRemove contains the string representation of
# the property, remove it
$AlsoRemove.Contains($obj.$_.ToString())
)
}
)
}
}
}
Note that the process block here automatically iterates a pipeline object, so the ForEach will only iterate more than once when an item is either explicitly passed in an array—such as by wrapping it in a single element array ,$array—or when provided as a direct argument, such as Remove-Null -InputObject $(Get-ChildItem).
It's also worth mentioning that both mine and batmanama's functions will remove these properties from each individual object. That is how it can properly utilize the PowerShell pipeline. Furthermore, that means that if any of the objects in the InputObject have a property that does not match (e.g. they are not null), an output table will still show that property, even though it has removed those properties from other items that did match.
Here's a simple example showing that behavior:
#([pscustomobject]#{Number=1;Bool=$true};
[pscustomobject]#{Number=2;Bool=$false},
[pscustomobject]#{Number=3;Bool=$true},
[pscustomobject]#{Number=4;Bool=$false}) | Remove-Null -AlsoRemove $false
Number Bool
------ ----
1 True
2
3 True
4

How to sort an object by keys using powershell

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

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

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.

ConvertTo-Json truncating object

I have a simple object with 1 parameter being an ArrayList of objects. I am using ConvertTo-Json to output this to Json. However even if I set -Depth 1000 I still see truncation of data.
Structure is:
Object
Property
Property - ArrayList of Object2.
Object 2 is a simple collection of properties.
The output I see is:
{
"CheckDate": "03 February 2016 10:12:30",
"Versions": [
{
},
{
}
]
}
Calling convert on the ArrayList directly all the data is shown. It would appear as if the -Depth argument is not being honored and is stuck at 2.
edit: Code to create object
$returnValue = New-Object System.Object
$returnValue | Add-Member -type NoteProperty -name CheckDate -value (Get-Date).DateTime
$versions = New-Object System.Collections.ArrayList
# This bit is in a loop.
$app = New-Object System.Object
$app | Add-Member -type NoteProperty -Name Name -Value $name
$app | Add-Member -type NoteProperty -Name Version -Value $version
$versions.Add($app)
# Back out of the loop.
$returnValue | Add-Member -type NoteProperty -name Versions -value $versions
Use PSObject instead of System.Object. Unfortunately, I cannot provide any details, it is some internal "magic" of ConvertTo-Json. Interestingly, it is enough to use PSObject instead of the second System.Object.