How to modify Json using Powershell - json

I have the following JSON held in a file "test.json":
{
"metadata": [
{
"src": [
{
"files": [
"src/**.csproj"
]
}
],
"dest": "api",
"disableGitFeatures": false,
"disableDefaultFilter": false
}
]
}
I'd like to modify the "src" element. Instead of:
"src": [
{
"files": [
"src/**.csproj"
]
}
],
It needs to be:
"src": [
{
"files": [
"*.csproj"
],
"cwd":".."
}
],
Where I modify the first element of "files" and add "cwd". This should be straight forward but I'm struggling to achieve this in powershell. Can anyone point me in the right direction of any examples of this?
Thanks for any pointers in advance.

You can do the following:
$JSONObject = Get-Content test.json -Raw | ConvertFrom-Json
$JSONObject.metadata.src.files = ,'*.csproj'
$JSONObject.metadata.src | Add-Member -Name 'cwd' -Value '..' -MemberType NoteProperty
$JSONObject | ConvertTo-Json -Depth 5 | Set-Content test.json
The tricky part is to make sure the .files value is an array of a single element. You can do this with the array subexpression operator #() or the unary operator ,.

Related

How do I add a JSON element if and only if it does not already exist, using Powershell?

I have a JSON file I need to edit, conditionally. It may be an empty object:
{}
or it may contain other data.
I need to see if the data I'd like to add already exists, and if not, add it to that JSON file.
The content in question looks like this (entire JSON file):
{
{
"2020.3.19f1": {
"version": "2020.3.19f1",
"location": [
"C:\\Program Files\\Unity\\Hub\\Editor\\2020.3.19f1\\Editor\\Unity.exe"
],
"manual": true
}
}
In this case, if "2020.3.19f" does not exist, I need to add that block.
I looked at these docs but really, lost. Any tips appreciated: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-json?view=powershell-7.2
This seems close but I'm lost on the syntax of checking for null or empty, and how that translates to PS: PowerShell : retrieve JSON object by field value
Edit: So, for example if the original file is:
{}
Then I need to overwrite that file with:
{
{
"2020.3.19f1": {
"version": "2020.3.19f1",
"location": [
"C:\\Program Files\\Unity\\Hub\\Editor\\2020.3.19f1\\Editor\\Unity.exe"
],
"manual": true
}
}
And if the file already contained something, I need to keep that, just add the new block:
{
{
"2019.4.13f1": {
"version": "2019.4.13f1",
"location": [
"C:\\Program Files\\Unity\\Hub\\Editor\\2019.3.13f1\\Editor\\Unity.exe"
],
"manual": true
},
{
"2020.3.19f1": {
"version": "2020.3.19f1",
"location": [
"C:\\Program Files\\Unity\\Hub\\Editor\\2020.3.19f1\\Editor\\Unity.exe"
],
"manual": true
}
}
FWIW: I did find the condition I need:
$FileContent = Get-Content -Path "C:\Users\me\AppData\Roaming\UnityHub\editors.json" -Raw | ConvertFrom-Json
if ( ($FileContent | Get-Member -MemberType NoteProperty -Name "2020.3.19f1") -ne $null )
{
echo "it exists"
}
else
{
echo "add it"
# DO SOMETHING HERE TO CREATE AND (OVER)WRITE THE FILE
}
You can convert the json as object and add properties as you want.
$json = #"
{
"2020.3.19f2": {
"version": "2020.3.19f2",
"location": [
"C:\\Program Files\\Unity\\Hub\\Editor\\2020.3.19f2\\Editor\\Unity.exe"
],
"manual": true
}
}
"#
$obj = ConvertFrom-Json $json
if (-not $obj.'2020.3.19f1') {
Add-Member -InputObject $obj -MemberType NoteProperty -Name '2020.3.19f1' -Value $(
New-Object PSObject -Property $([ordered]#{
version = "2020.3.19f1"
location = #("C:\Program Files\Unity\Hub\Editor\2020.3.19f1\Editor\Unity.exe")
manual = $true
})
) -Force
$obj | ConvertTo-Json
}

Get value by the array element name in json

I'm not sure how to name these elements properly, it'll be easier just to show it. I have following JSON:
{
"DEV": [
{
"GitEmail": "asd#asd.com"
}
],
"TEST": [
{
"GitEmail": "asd1#asd.com"
}
],
"PROD": [
{
"GitEmail": "asd2#asd.com"
}
]
}
I would like to get the "DEV" by providing it's email. How to implement that in powershell?
Something like below can help -
PS> $json = '{
"DEV": [
{
"GitEmail": "asd#asd.com"
}
],
"TEST": [
{
"GitEmail": "asd1#asd.com"
}
],
"PROD": [
{
"GitEmail": "asd2#asd.com"
}
]
}' | ConvertFrom-Json
PS> ($json.psobject.Properties | ? {$_.Value -match "asd#asd.com"}).Name
Depending on the email matches you can retrieve the environment names.
I can't promise there is an easier method, but this here is one way:
Given that you json is stored in a variable $json:
You can get every head object with $json.psobject.properties.name:
Input:
$json.psobject.properties.name
Output:
DEV
TEST
PROD
With this we can create a foreach loop and search for the Email:
foreach ($dev in $json.psobject.properties.name)
{
if($json.$dev.GitEmail -eq "asd#asd.com") {
echo $dev
}
}
I do not know any elegant way of doing it. ConvertFrom-Json does not create neat objects with easy ways to traverse them like convertfrom-xml, instead result is just a PsObject with bunch of noteproperties.
What I do in such cases is
$a= #"
{
"DEV": [
{
"GitEmail": "asd#asd.com"
}
],
"TEST": [
{
"GitEmail": "asd1#asd.com"
}
],
"PROD": [
{
"GitEmail": "asd2#asd.com"
}
]
}
"#
$JsonObject= ConvertFrom-Json -InputObject $a
$NAMES= $JsonObject|Get-Member |WHERE MemberType -EQ NOTEPROPERTY
$NAMES|Foreach-Object {IF($JsonObject.$($_.NAME).GITEMAIL -EQ 'asd#asd.com'){$_.NAME}}
Result of above is
DEV
Not pretty, not really re-usable but works.
If anyone knows a better way of going about it - I'll be happy to learn it:)

Editing a .json file using powershell

I have a .json file that needs to be edited in User Data, so I will have to use powershell to accomplish this.
The json looks something like this:
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
{
"Id": "CustomLogs",
"FullName": "AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch",
"Parameters": {
"LogDirectoryPath": "C:\\CustomLogs\\",
"TimestampFormat": "MM/dd/yyyy HH:mm:ss",
"Encoding": "UTF-8",
"Filter": "",
"CultureName": "en-US",
"TimeZoneKind": "Local"
}
}
],
"Flows": {
"Flows":
[
"(ApplicationEventLog,SystemEventLog),CloudWatchLogs"
]
}
}
}
I would like it to look like this --
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
{
"Id": "CustomLogs",
"FullName": "AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch",
"Parameters": {
"LogDirectoryPath": "C:\\ProgramData\\Amazon\\CodeDeploy\\deployment-logs",
"TimestampFormat": "[yyyy-MM-dd HH:mm:ss.fff]",
"Encoding": "UTF-8",
"Filter": "",
"CultureName": "en-US",
"TimeZoneKind": "Local"
}
}
],
"Flows": {
"Flows":
[
"(ApplicationEventLog,SystemEventLog, CustomLogs),CloudWatchLogs"
]
}
}
}
In the Custom Logs Parameters, the LogDirectoryPath and TimestampFormat have both changed. Also, in the Flows section, I have added the 'CustomLogs' to the CloudWatch Group.
I tried making it work with code like this:
$a = Get-Content 'C:\PATH\TO\file.json' -raw | ConvertFrom-Json
$a.EngineConfiguration.Components[0].Parameters = '{"LogDirectoryPath": "","TimestampFormat": "[yyyy-MM-dd HH:mm:ss.fff]","Encoding": "UTF-8","Filter": "","CultureName": "en-US","TimeZoneKind": "Local"}'
$a | ConvertTo-Json | set-content 'C:\PATH\TO\output.json'
But that produces a very ugly output
{
"EngineConfiguration": {
"PollInterval": "00:00:15",
"Components": [
"#{Id=CustomLogs; FullName=AWS.EC2.Windows.CloudWatch.CustomLog.CustomLogInputComponent,AWS.EC2.Windows.CloudWatch; Parameters={\"LogDirectoryPath\": \"\",\"TimestampFormat\": \"[yyyy-MM-dd HH:mm:ss.fff]\",\"Encoding\": \"UTF-8\",\"Filter\": \"\",\"CultureName\": \"en-US\",\"TimeZoneKind\": \"Local\"}}",
"#{Id=CloudWatchLogs; FullName=AWS.EC2.Windows.CloudWatch.CloudWatchLogsOutput,AWS.EC2.Windows.CloudWatch; Parameters=}"
],
"Flows": {
"Flows": "(ApplicationEventLog,SystemEventLog),CloudWatchLogs"
}
}
}
Is there a more elegant way to do this? Any advice would be greatly appreciated. Thanks!
Try using the -Depth switch for ConvertTo-Json. By default this compresses any child elements beyond a depth of 2 to the string representations of the object you have seen:
"#{Id=CustomLogs; etc."
By specifying a deeper depth you get a format more like the one you want. Combine this with something that compresses the excessive whitespace as so:
((ConvertFrom-Json $a) | ConvertTo-Json -Depth 4) -replace ((" "*4)," ")
It would be possible to reduce the leading whitespace with a regex. However, that does not really produce the reformatting, pretty-print that you say you want.
$a | ConvertTo-Json | % {$_ -replace " "," "} | set-content 'output.json'

Go over json data in Powershell

I have a JSON file that looks like this:
$jsondata = '{
"ips": {
"10.20.30.40": [
{
"rhost": "DNS Name1.",
"rdata": [
"10.20.30.40"
],
"rrtype": "A (1)",
"ttl": 86400,
"geo": null,
"source": "DNSProvider1"
}
],
"40.50.60.70": [
{
"rhost": "DNS Name2.",
"rdata": [
"40.50.60.70"
],
"rrtype": "A (1)",
"ttl": 86400,
"geo": null,
"source": "DNSProvider1"
}
]
}
}'
I want to get all the TTLs (for example) of every IP address in the list.
I converted this JSON to Powershell PSCustomObject:
$obj = $jsondata | convertFrom-Json
and now I want to get all the TTLs, I tried to get the list of the IPs (as a start):
foreach ($ip in $a.ips) {write-host $ip }
and I'm not getting strings as a result, that's why I (probably) can't go inside and get the TTLs.
So my question: how can I get all the IPs as strings?
I believe that once I'll get an answer for that, I'll understand how I can go over all the IPs in the list.
Thanks!
foreach($ip in $obj.ips | Get-Member -MemberType NoteProperty)
{
Write-Host -Verbose ("IP Address {0} has TTL {1}" -f $ip.Name, $obj.ips."$($ip.Name)".ttl)
}
Get-Member will get you the name of the property (which is the ip address) and not the value.
Thanks Rubanov, that really helped!
And just to document the whole answer:
foreach($ip in $obj.ips | Get-Member -MemberType NoteProperty)
{
Write-Host -Verbose $obj.ips.$($ip.Name).ttl
}
Or:
($obj.ips | Get-Member -MemberType NoteProperty).Name | % {$obj.ips.$_.ttl}

ConvertFrom-JSON strips array from object

I'm loading a text file containing some Json to edit a property. However, after modifying the content and writing it to file, the Json becomes invalid.I
I use the following PowerShell to modify the file:
$manifest = Get-Content $PathToManifest -Raw | ConvertFrom-Json
#modify Json
Set-Content -Path $PathToManifest -Value ( $manifest | ConvertTo-Json)
The following snippet from my Json file gets corrupted:
"contributions": [
{
"id": "sample-data-widget",
"type": "ms.vss-dashboards-web.widget",
"targets": ["ms.vss-dashboards-web.widget-catalog"],
"properties": "#{name=Sample Data; description=Create sample data in a VSTS project.; previewImageUrl=img/logo.png; uri=index.html; supportedSizes=System.Object[]; supportedScopes=System.Object[]}"
}]
After loading the Json and writing it back to file the array syntax around targets is gone:
"contributions": [
{
"id": "sample-data-widget",
"type": "ms.vss-dashboards-web.widget",
"targets": "ms.vss-dashboards-web.widget-catalog",
"properties": "#{name=Sample Data; description=Create sample data in a VSTS project.; previewImageUrl=img/logo.png; uri=index.html; supportedSizes=System.Object[]; supportedScopes=System.Object[]}"
}]
Why is this happening? Is there a way to make sure the syntax doesn't change?
ConvertTo-Json has Depth parameter that controls how many levels of contained objects are included in the JSON representation. The default value is 2. ConvertTo-Json will call .ToString() on anything nested deeper than specified Depth.
So all you need is to specify sufficiently large number for Depth argument or just ([int]::MaxValue).
Set-Content -Path $PathToManifest -Value ( $manifest | ConvertTo-Json -Depth ([int]::MaxValue))
Examples of nesting and ConvertTo-Json behavior:
$NestedArray = #(1,#(2,#(3,#(4))))
Default:
$NestedArray | ConvertTo-Json
[
1,
{
"value": [
2,
[
3,
"4"
]
],
"Count": 2
}
]
No nesting at all:
$NestedArray | ConvertTo-Json -Depth 1
[
1,
{
"value": [
2,
"3 System.Object[]"
],
"Count": 2
}
]
Desired result:
$NestedArray | ConvertTo-Json -Depth 3
[
1,
{
"value": [
2,
[
3,
[
4
]
]
],
"Count": 2
}
]