I am working with a JSON like that looks like this:
[
{
"Ack": "no",
"Rule": "dont",
"Tags": [
"server"
],
"Type": "blue"
},
{
"Ack": "no1",
"Rule": "knock",
"Tags": [
"yellow",
"green"
],
"Type": "multiplecolour"
}
]
I need to convert the Tags array into a comma-separated string [and replace the array with the converted string in the JSON file]. I have tried converting from JSON, but I am struggling to convert the array into string in a clean way, still learning PS so please bear with me.
ConvertFrom-Json may work for you. Here's an example of converting your JSON string to an array of PowerShell objects, then joining the tags for each object with a comma delimiter:
$json = #"
[
{
"Ack": "no",
"Rule": "dont",
"Tags": [
"server"
],
"Type": "blue"
},
{
"Ack": "no1",
"Rule": "knock",
"Tags": [
"yellow",
"green"
],
"Type": "multiplecolour"
}
]
"#
(ConvertFrom-Json -InputObject $json) `
| ForEach-Object { $_.Tags = ($_.Tags -join ","); $_ } `
| ConvertTo-Json `
| Out-File -FilePath new.json
EDIT: Note (as #mklement0 points out), the parentheses around ConvertFrom-Json are required to force the enumeration of the results as an array of objects through the pipeline.
Related
I want to get specific value once key is matched from JSON array object
Input:
[
{
"Id": "19A7A3C4",
"displayName": "somename",
"tags": [
{
"context": "CONTEXTLESS",
"key": "apple",
"value": "10"
},
{
"context": "CONTEXTLESS",
"key": "orange",
"value": "20"
},
{
"context": "CONTEXTLESS",
"key": "grapes",
"value": "30"
}
]
},
{
"Id": "111111",
"displayName": "somename",
"tags": [
{
"context": "CONTEXTLESS",
"key": "cat",
"value": "10"
},
{
"context": "CONTEXTLESS",
"key": "cat",
"value": "20"
}
]
}
]
I want to get the value of tag where key matches to cat and value matches to 10, I am using below query but getting whole object
$content = Get-Content -Raw -Path "data.json" | ConvertFrom-Json
$content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}
Desired Output: 10
Mathias's answer already shows a clean way to solve the problem.
A similar approach is using the intrinsic method .Where{}:
$tagValues = $content.tags.Where{ $_.key -eq "cat" -and $_.value -eq "10" }.value
$content.tags employs member enumeration to collect all tags properties into an array. .Where{} filters array elements similar to Where-Object. Lastly .value uses member enumeration again to collect the filtered tag values into an array.
Intrinsic methods like .Where{} are supposed to be faster than pipeline commands because they don't involve the overhead of the pipeline machinery.
If you want to keep your original query, you have to deal with nested properties.
Use grouping operator () and dot notation to extract a given property:
$tagValues = ($content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"}).tags.value
An alternative is Select-Object with parameter -ExpandProperty (alias -Expand), but it doesn't work as straightforward for nested properties (yet):
$tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
Select-Object -Expand tags | Select-Object -Expand value
A more straightforward alternative is ForEach-Object:
$tagValues = $content | Where-Object{ $_.tags.key -eq "cat" -and $_.tags.value -eq "10"} |
ForEach-Object { $_.tags.value }
Enumerate all tags, then use ForEach-Object to grab just the value property of any match:
$content.tags | Where-Object { $_.key -eq "cat" -and $_.value -eq "10"} |ForEach-Object -MemberName value
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.
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"
}
]
}
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'
}
}
Im starting to learn PowerShell and have been trying to create a foreach loop so that if one of the JSON items has a status other than STARTED, it runs a command using its name as a variable in the executable command. Here is what my json txt file looks like;
{
"UNIT": {
"name": "AB",
"address": "fadasdaer",
"status": "MIA"
},
"UNIT": {
"name": "CD",
"address": "fadasdahsfaaer",
"status": "STARTED"
},
"UNIT": {
"name": "EF",
"address": "9afahegt",
"status": "DEAD"
}
}
And what I am trying to do is read this from my json.txt and get it to run a foreach loop and execute a command where the name is incorporated in the command. I currently have something like this, but my PowerShell understand is limited and it doesnt work...
$JSON = json.txt
$check = $JSON | ConvertFrom-Json
$started=STARTED
foreach($unit in $check.unit){
if ($unit.status -notmatch $started) {
$name=$unit.name
executable.exe start $name
}
}
Any guidance would be greatly appreciated.
Your primary problem is that your JSON is malformed: it defines a single object and then defines its UNIT property multiple times.
You should define it as an array: note the enclosing top-level [...] and the absence of UNIT properties:
[
{
"name": "AB",
"address": "fadasdaer",
"status": "MIA"
},
{
"name": "CD",
"address": "fadasdahsfaaer",
"status": "STARTED"
},
{
"name": "EF",
"address": "9afahegt",
"status": "DEAD"
}
]
With both the JSON input and your other syntax problems corrected:
$JSON = 'json.txt'
$check = Get-Content -Raw $JSON | ConvertFrom-Json
$started = 'STARTED'
foreach ($unit in $check) {
if ($unit.status -notmatch $started) {
$name = $unit.name
executable.exe start $name
}
}
If you cannot fix the JSON at the source, you can transform it yourself before passing it to ConvertFrom-Json:
$check = (Get-Content -Raw $JSON) `
-replace '\A\{', '[' `
-replace '\}\Z', ']' `
-replace '"UNIT": ' | ConvertFrom-JSON