Powershell converting array to json yields unexpected results - arrays

I'm trying to dump a powershell array of hash tables to json like this:
$body= ,#($someArray | % {
{
#{
name = $_
}
}
})
$body | ConvertTo-Json
But this yields this result:
{
"value": [
{
"name": "test"
},
{
"name": "test-2"
}
],
"Count": 2
}
Not sure how to only get the value.
If I do ConvertTo-Json | $body, I get:
[
[
{
"name": "test",
},
{
"name": "test-2",
}
]
]
I'm doing the ,#() syntax because otherwise if I only have one object in my array it gets converted to an Hash Table instead of an array.
I am on Powershell 5.

Related

How to add objects to a blank array in a json file using powershell

here is my json body .
{
"source": 2,
"revision": 3,
"description": null,
"triggers": [],
"releaseNameFormat": "Release-$(rev:r)",
"tags": [],
"pipelineProcess": {
"type": 1
},
"properties": {
"DefinitionCreationSource": {
"$type": "System.String",
"$value": "BuildSummary"
},
"System.EnvironmentRankLogicVersion": {
"$type": "System.String",
"$value": "2"
}
},
"id": 5,
"name": "CheckListAPI - CD",
"path": "\\Admin",
"projectReference": null,
"url": "",
"_links": {
"self": {
"href": ""
},
"web": {
"href": ""
}
}
}
I want to add some values inside the brackets at "triggers": [],
What I'm trying to get is:
"triggers":
[
{
"artifactAlias": "_DV_NJ_PIPE",
"triggerConditions": [],
"triggerType": 1
}
],
i tried -replace and replace() saving the json file to local system, but none of them are working, I even tried to edit the json file directly like this but failed.
$alias = $json.triggers
foreach ($artifact in $alias )
{
$artifact.artifactAlias = "_$DefName"
$artifact.triggerConditions = "{}"
$artifact.triggertype = "artifactSource"
}
Please help.
You can import the json file as PowerShell objects, manipulate the structure until it looks the way you want it to and export it back to json format:
$pipeline = Get-Content .\input.json | ConvertFrom-Json
$trigger = [ordered]#{
artifactAlias = "_DV_NJ_PIPE"
triggerConditions = #()
triggerType = 1
}
$pipeline.triggers += $trigger
$pipeline | ConvertTo-Json -Depth 5 | Out-File .\output.json
As it was pointed out in the comments, it is of course also possible to import the trigger definition from a json file instead of building it in a hash table.

jq - subtracting one array from another using a single command

I have three operations with jq to get the right result. How can I do it within one command?
Here is a fragment from the source JSON file
[
{
"Header": {
"Tenant": "tenant-1",
"Rcode": 200
},
"Body": {
"values": [
{
"id": "0b0b-0c0c",
"name": "NumberOfSearchResults"
},
{
"id": "aaaa0001-0a0a",
"name": "LoadTest"
}
]
}
},
{
"Header": {
"Tenant": "tenant-2",
"Rcode": 200
},
"Body": {
"values": []
}
},
{
"Header": {
"Tenant": "tenant-3",
"Rcode": 200
},
"Body": {
"values": [
{
"id": "cccca0003-0b0b",
"name": "LoadTest"
}
]
}
},
{
"Header": {
"Tenant": "tenant-4",
"Rcode": 200
},
"Body": {
"values": [
{
"id": "0f0g-0e0a",
"name": "NumberOfSearchResults"
}
]
}
}
]
I apply two filters and create two intermediate JSON files. First I create the list of all tenants
jq -r '[.[].Header.Tenant]' source.json >all-tenants.json
And then I select to create an array of all tenants not having a particular key present in the Body.values[] array:
jq -r '[.[] | select (all(.Body.values[]; .name !="LoadTest") ) ] | [.[].Header.Tenant]' source.json >filter1.json
Results - all-tenants.json
["tenant-1",
"tenant-2",
"tenant-3",
"tenant-4"
]
filter1.json
["tenant-2",
"tenant-4"
]
And then I substruct filter1.json from all-tenants.json to get the difference:
jq -r -n --argfile filter filter1.json --argfile alltenants all-tenants.json '$alltenants - $filter|.[]'
Result:
tenant-1
tenant-3
Tenant names - values for the "Tenant" key are unique and each of them occurs only once in the source.json file.
Just to clarify - I understand that I can have a select condition(s) that would give me the same resut as subtracting two arrays.
What I want to understand - how can I assign and use these two arrays into vars directly in a single command not involving the intermediate files?
Thanks
Use your filters to fill in the values of a new object and use the keys to refer to the arrays.
jq -r '{
"all-tenants": [.[].Header.Tenant],
"filter1": [.[]|select (all(.Body.values[]; .name !="LoadTest"))]|[.[].Header.Tenant]
} | .["all-tenants"] - .filter1 | .[]'
Note: .["all-tenants"] is required by the special character "-" in that key. See the entry under Object Identifier-Index in the manual.
how can I assign and use these two arrays into vars directly in a single command not involving the intermediate files?
Simply store the intermediate arrays as jq "$-variables":
[.[].Header.Tenant] as $x
| ([.[] | select (all(.Body.values[]; .name !="LoadTest") ) ] | [.[].Header.Tenant]) as $y
| $x - $y
If you want to itemize the contents of $x - $y, then simply add a final .[] to the pipeline.

Replace symbol in JSON with different array values

I have 2 files
text.json that contains
{
"Files": [
{
"pattern": "/Something/Something/*"
},
{
"pattern": "/Something/Something/*"
},
{
"pattern": "/Something/Something/*"
},
{
"pattern": "/Something/Something/*"
},
{
"pattern": "/Something/Something/*"
},
{
"pattern": "/Something/Something/*"
}
]
}
and dlls.txt
1.dll
2.dll
..
6.dll
I want to replace the symbol * with the necessary dll like this :
"Files": [
{
"pattern": "/Something/Something/1.dll"
},
{
"pattern": "/Something/Something/2.dll"
},
.
.
.
{
"pattern": "/Something/Something/6.dll"
}
]
}
So far my code replaces the symbol but only with the last array element.
Since you're dealing with a structured data format - JSON - using a dedicated parser is always preferable to performing purely textual processing based on regexes.
While using the dedicated ConvertFrom-Json and ConvertTo-Json cmdlets to parse from and serialize back to JSON is slower than textual processing, it is much more robust.
# Read the DLL names from the text file into an array of strings.
$dlls = Get-Content dlls.txt
# Read the JSON file and parse it into an object.
$objFromJson = Get-Content -Raw text.json | ConvertFrom-Json
# Loop over all elements of the array in the .Files property and
# update their .pattern property based on the corresponding DLL names.
$i = 0
$objFromJson.Files.ForEach({
$_.pattern = $_.pattern -replace '(?<=/)\*$', $dlls[$i++]
})
# Convert the updated object back to JSON; save to a file as needed.
$objFromJson | ConvertTo-Json
Why not skip the 'C:\Users\itsan\Desktop\text.json' file alltogether and simply create a new JSON from the dll filenames you have in 'C:\Users\itsan\Desktop\dlls.txt' ?
$dlls = Get-Content -Path 'C:\Users\itsan\Desktop\dlls.txt'
$result = [PsCustomObject]#{
Files = foreach($file in $dlls) {
"" | Select-Object #{Name = 'pattern'; Expression = {"/Something/Something/$file"}}
}
}
$result | ConvertTo-Json
If you want that as new file, simply change the last line into
$result | ConvertTo-Json | Set-Content -Path 'C:\Users\itsan\Desktop\dll.json'
Output wil be like this:
{
"Files": [
{
"pattern": "/Something/Something/1.dll"
},
{
"pattern": "/Something/Something/2.dll"
},
{
"pattern": "/Something/Something/3.dll"
},
{
"pattern": "/Something/Something/4.dll"
},
{
"pattern": "/Something/Something/5.dll"
},
{
"pattern": "/Something/Something/6.dll"
}
]
}

PowerShell ConvertTo-JSON with multiple arrays

I'm trying to convert data to JSON as input for a REST API. The challenge I have is that the data should consist of multiple depths (For a lack of better words). The code I'm using now is:
(#{name = "Contoso"; all_assets = "false"; all_users="false"; rules= #{type="fqdn"; operator="match"; terms=#("contoso") } }| ConvertTo-Json)
the output now is:
{
"all_users": "false",
"name": "Contoso",
"all_assets": "false",
"rules": {
"operator": "match",
"terms": [
"contoso"
],
"type": "fqdn"
}
}
The REST-Api is complaining that the data contains invalid characters. Looking at the output, the section "rules:" contains { } instead of [ ]. I've been trying all kinds of tricks but I can't seem to figure this one out.
Anyone know what I'm doing wrong here?
If you want rules to contain an array of objects instead of an object with properties, enclose everything that goes inside the rules with #().
Because terms then becomes the 3rd level, you need to add parameter -Depth to the ConvertTo-Json cmdlet:
For better readability, I didn't do this as one-liner
#{
name = "Contoso"
all_assets = "false"
all_users = "false"
rules = #(
#{
type = "fqdn"
operator = "match"
terms = #("contoso")
}
)
} | ConvertTo-Json -Depth 3
Output:
{
"all_users": "false",
"name": "Contoso",
"all_assets": "false",
"rules": [
{
"operator": "match",
"terms": [
"contoso"
],
"type": "fqdn"
}
]
}
For what it's worth...
Not the answer to the Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2 issue, but how you might generally build a PowerShell expression from a Json file using ConvertTo-Expression cmdlet:
'{
"all_users": "false",
"name": "Contoso",
"all_assets": "false",
"rules": [
{
"operator": "match",
"terms": [
"contoso"
],
"type": "fqdn"
}
]
}' | ConvertFrom-Json | ConvertTo-Expression
[pscustomobject]#{
'all_users' = 'false'
'name' = 'Contoso'
'all_assets' = 'false'
'rules' = ,[pscustomobject]#{
'operator' = 'match'
'terms' = ,'contoso'
'type' = 'fqdn'
}
}

Delete on nested array with jq

this is my data structure:
[
{
"name": "name1",
"organizations": [
{
"name": "name2",
"spaces": [
{
"name": "name3",
"otherkey":"otherval"
},
{
"name": "name4",
"otherkey":"otherval"
}
]
}
]
},
{
"name": "name21",
"organizations": [
{
"name": "name22",
"spaces": [
{
"name": "name23",
"otherkey":"otherval"
},
{
"name": "name24",
"otherkey":"otherval"
}
]
}
]
}
]
i just want to keep name=name1, remove the nested array object with name=name4 and want to keep the rest of the object intact. I tried with map(select) but this will just give me the full object. Is it possible to work with del on specific subarrays and keep the rest as it is?
result should be the following. in addition i want to avoid enumeration all attributes to keep on outer objects:
[
{
"name": "name1",
"organizations": [
{
"name": "name2",
"spaces": [
{
"name": "name3",
"otherkey":"otherval"
}
]
}
]
}
]
any idea? thanks!
A very targeted solution would be:
path(.[0].organizations[0].spaces) as $target
| (getpath($target) | map(select(.name != "name4"))) as $new
| setpath($target; $new)
If permissible, though, you might consider:
walk(if type == "object" and .spaces|type == "array"
then .spaces |= map(select(.name != "name4"))
else . end)
or:
del(.. | .spaces? // empty | .[] | select(.name == "name4") )
(If your jq does not have walk/1 then its jq definition can easily be found by googling.)
You can use the below and it will remove the "name": "name4" array only.
jq 'del(.[] | .organizations? | .[] | .spaces?|.[] | select(.name? == "name4"))' yourJsonFile.json
Here is a solution using select, reduce, tostream and delpaths
map(
select(.name == "name1")
| reduce (tostream|select(length==2)) as [$p,$v] (
.
; if [$p[-1],$v] == ["name","name4"] then delpaths([$p[:-1]]) else . end
)
)
I took a similar approach as #peak but inverted it, so instead of selecting what you want and setting that in the output we're selecting what we don't want and deleting it.
[path(.organizations[0].spaces[]| select(.name == "name4")] as $trash | delpaths($trash)

Resources