This question already has answers here:
Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2
(2 answers)
Closed 2 years ago.
This question has also been answered here:
Stackoverflow: Unexpected ConvertTo-Json results? Answer: it has a default -Depth of 2
GitHub: ConvertFrom-Json and ConvertTo-Json breaks arrays
Mircrosoft Docs: ConvertTo-JSON
TL;DR
If you save your .json with ConvertTo-JSON and it break it, you may want to speccify the -Depth parameter, as it's default value it 2 and it wont parse nestet objects beyond that.
Original Post:
So I want to load, then save data from a JSON file through a PowerShell skript. The JSON contains an array within an object, which looks like this:
{
"head": {
"head2": {
"data0": "a",
"data1": "b"
},
"head3": {
"data8": [
"x",
"y",
"z"
],
"data9": "hello"
}
}
}
Notice the array "data8".
Now when I load and save the file like so: Get-Content test.json | ConvertFrom-JSON | ConvertTo-JSON I want to end up with the exact same file, since I'm not changing anything. However the result is this:
{
"head": {
"head2": {
"data0": "a",
"data1": "b"
},
"head3": {
"data8": "x y z",
"data9": "hello"
}
}
}
The "data8" has been reduced to a single string and I struggle to find out why. It seems to happen during the ConvertTo-JSON because when I don't do that yet, It will give me an array of strings containing x,y and z.
E.g. (Get-Content test.json | ConvertFrom-JSON).head.head3.data8 will result in
x
y
z
and (Get-Content test.json | ConvertFrom-JSON).head.head3.data8 -is [array] gives True
Things I've tried:(get-content test.json | convertfrom-json).head.head3.data8
Various encoding methods in test.json, but this even happens when I save the file as utf8 and add -encoding utf8 to Get-Content so I don't believe that is has to do with that.
For the sake of trying, I even added -compress to ConvertTo-JSON which didn't help either.
Okay so apparently PowerShell only parses data up to a depth of 2 by default. So as pointed out Here you have to specify the depth with the depth prameter of ConvertTo-Json
Related
How can I query change value of the "type" string in my JSON file with PowerShell? I can't get to the "type" string.
JSON file
{
"name": "b",
"compatibilityLevel": 1400,
"model": {
"culture": "c",
"dataSources":[
{
"type": "structured"
}
]
}}
PowerShell
$pathToJson = "C:\Model.bim"
$a = Get-Content $pathToJson | ConvertFrom-Json
$a.'model.dataSources.type' = "c"
$a | ConvertTo-Json -Depth 10 | Set-Content $pathToJson
tl;dr
$a.model.dataSources[0].type = 'c'
Note the need to specify index [0], because $a.model.dataSources is an array.
AS for what you tried:
$a.'model.dataSources.type' = "c"
You cannot use a property path stored in a string ('...') to directly access a nested property, because PowerShell interprets 'model.dataSources.type' as the name of a single property.
See this answer for workarounds.
Even with that problem corrected, $a.model.dataSources.type = "c" does not work, because $a.model.dataSources returns an array of values, and you cannot directly set a property on that array's elements; instead you must explicitly target the array element of interest, as shown above ([0]).
Note you can get the array elements' .type values with $a.model.dataSources.type, via a PSv+ feature called member-access enumeration, but that doesn't work on setting - by design, to prevent possibly inadvertent updating of all array elements with the same value; see this answer.
This question already has answers here:
Cannot convert PSCustomObjects within array back to JSON correctly
(2 answers)
Closed 6 years ago.
In powershell, the following produces correct results.
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": [
"herp",
"derp"
]
}
}
However if I add one more level, then the array "buzz" is flattened into a string
$foo = #{}
$foo.bar = #{}
$foo.bar.buzz = #{}
$foo.bar.buzz.bazz = #("herp","derp")
ConvertTo-Json $foo
{
"bar": {
"buzz": {
"bazz": "herp derp"
}
}
}
Why does powershell flatten Arrays into strings?
This issue feels similar to the following SO questions, but none of the purposed solutions solves it.
why-does-powershell-silently-convert-a-string-array-with-one-item-to-a-string
what-determines-whether-the-powershell-pipeline-will-unroll-a-collection
From ConvertTo-Json documentation
-Depth
Specifies how many levels of contained objects are included in the
JSON representation. The default value is 2.
Adding -Depth 9 option to ConvertTo-Json fixes the issue
ConvertTo-Json -Depth 9 $foo
Also, there is a difference between ConvertTo-Json $foo and $foo | ConvertTo-Json. Use ConvertTo-Json $foo whenever possible.
convertto-json-an-array-with-a-single-item
I have, for complicated reasons involving a trip from an Apple plist through xml2json, a number of JSON files with data in this form:
{
"key": [ "key1", "key2", "key3" ],
"string": [ "value1", "value2", "value3" ]
}
And I would like to convert that to a normal JSON object:
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
After some head-banging, I came up with this jq program to do the trick:
jq '. as $d|[range(.key|length)|{"key":$d.key[.],"value":$d.string[.]}]|from_entries'
That works, but it seems a little convoluted. I was wondering if there were a cleaner solution?
This is indeed similar to this question, but the difference is that this is an object with named key and value elements instead just an array containing the two arrays directly.
The script you provide is already pretty good! This variation of your script saves the index into the variable instead of the input object, which feels more natural to read for me. It then creates an array of one key objects and adds them together.
jq '[range(.key | length) as $i | {(.key[$i]): .string[$i]}] | add'
When I first looked at this issue, I though that a zip builtin would improve the situation. Then I remembered: there is already a zip builtin! It's just called transpose. Using it, you can create a script such as this:
jq '[.key, .string] | transpose | map({key: .[0], value: .[1]}) | from_entries'
It seems easier to follow to me as well, although it is quite long too; I assume, however, that the focus is readability and not character count. Of course, you can also mix up both solutions:
jq '[.key, .string] | transpose | map({(.[0]): .[1]}) | add'
Here is a solution which uses reduce with a state object holding an iteration index and a result object. It iterates over .key setting corresponding values in the result from .string
.string as $v
| reduce .key[] as $k (
{idx:0, result:{}}; .result[$k] = $v[.idx] | .idx += 1
)
| .result
I'm trying to modify a badly formated JSON Textfile, that currently looks like this:
[
["val1", "val2", "val3", "val4"],
["val5", "val6", "val7", "val8"],
["val9", "val10", "val11", "val12"]
]
and I have an other array containing field names
["title1", "title2", "title3", "title4"]
I want to output a final.json textfile looking like this:
[
{"title1": "val1", "title2": "val2", "title3": "val3", "title4": "val4"},
{"title1": "val5", "title2": "val6", "title3": "val7", "title4": "val8"},
{"title1": "val9", "title2": "val10", "title3": "val11", "title4": "val12"}
]
I guess the best way would be to take each row, split by , and then adding them back together foreach-ing over the title names, but I'm not quite sure on how to do this in PowerShell.
Since you're dealing with structured data here, I think the best way is to parse the JSON, and work with the resulting objects. Create the objects you want, then convert back to JSON:
$j1 = #'
[
["val1", "val2", "val3", "val4"],
["val5", "val6", "val7", "val8"],
["val9", "val10", "val11", "val12"]
]
'#
$j2 = #'
["title1", "title2", "title3", "title4"]
'#
$a1 = $j1 | ConvertFrom-Json
$a2 = $j2 | ConvertFrom-Json
0..($a1.Count-1) | ForEach-Object {
$i = $_
$props = #{}
0..($a2.Count-1) | ForEach-Object {
$props[$a2[$_]] = $a1[$i][$_]
}
New-Object PSOBject -Property $props
} | ConvertTo-Json
ConvertTo-Json and ConvertFrom-Json are the cmdlets you need to serialize/deserialize the JSON. Then you just work with the objects.
In this case I'm going through each top level array in $a1 and creating a hashtable that contains the properties you want. Then I'm creating a PSObject with those properties. That gets returned from the ForEach-Object cmdlet (the result is an array of those objects) which then gets piped directly into ConvertTo-Json to give you the output needed.
I think a better approach is to read in the first JSON format via ConvertFrom-Json, then take that array of arrays and for each row, create a PSCustomObject from a hashtable e.g. [PSCustomObject]#{title1=$arr[$row][0]; title2=$arr[$row][1];...}. Once you then have that array of PSCustomObject, convert that back to JSON with ConvertTo-Json.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Comparing two arrays & get the values which are not common
I wanted a logic to get uncommon items from an array, for example:
$a=#(1,2,3,4,5,6)
$b=#(1,2,3,4,5,7,9,10)
I want the output $c to be 6 which is the missing element in $b array, priority should be only given to the array contents of $a.
Can anyone please help me out with this?
Thanks!
Either empo's approach, or
$a1=#(1,2,3,4,5,8)
$b1=#(1,2,3,4,5,6)
Compare-Object $a1 $b1 |
Where-Object { $_.SideIndicator -eq '<=' } |
Foreach-Object { $_.InputObject }
returns 8
$c = $a | ? {!($b -contains $_)}
The priority will be given to the variable you "pipe".