I have two variables of Byte[]
type (I chose this type for a specific need, so it need to be retained.) which are declared as:
$first = New-Object Byte[] 32
$second = New-Object Byte[] 32
and, I initalized each index of both the variables.
Now, I created a hashtable $List1
as:
$List1=@{"First" = $first; "Second" = $second}
I am using the syntax below for creating the json file:
$List1 | ConvertTo-Json | Set-Content -Path $jsonFile1
This is the first json file content:
{
"First": {
"value": [
210,
195,
131,
176,
88,
154,
57,
37,
2,
75,
182,
190,
156,
43,
113,
199,
63,
25,
109,
92,
220,
91,
219,
252,
113,
68,
202,
12,
147,
194,
36,
177
],
"Count": 32
},
"Second": {
"value": [
238,
225,
12,
172,
134,
94,
42,
204,
27,
78,
39,
166,
229,
111,
143,
254
],
"Count": 16
}
}
Then I read the first json file into a temporary variable as below:
$tempHash = Get-Content -Path $jsonFile1 -Raw| ConvertFrom-Json
Since $tempHash
is a PSCustomObject
, I create a new hashtable $List2
as below:
$List2 = @{"First" = $tempHash.First.value; "Second"= $tempHash.Second.value}
which I use to create the second json file as below:
$List2 | ConvertTo-Json | Set-Content -Path $jsonFile2
This is the second json file content:
{
"First": [
133,
231,
19,
173,
60,
50,
105,
68,
38,
109,
99,
155,
2,
188,
216,
9,
8,
225,
203,
15,
167,
8,
188,
76,
192,
154,
183,
194,
1,
122,
143,
137
],
"Second": [
27,
3,
57,
67,
155,
145,
181,
194,
250,
10,
65,
90,
41,
230,
243,
196
]
}
I am using the same syntax to create both the json files. So, why are their structures different?
[Edit]
I suspect, the difference is because of this very syntax:
$List1=@{"First" = $first; "Second" = $second}
because Byte[]
type variable does not work as a simple integer[]
type variable. Correct me.
[Edit]
So, it turns out Byte[]
type variable has two different keys. "value" which holds the actual array of byte values, and "Count" which holds the number of elements in the Byte[]
variable. However, when we invoke the Byte[]
type variable like:
$first
which is of Byte[]
type, we get only the values listed under the "value" key. The value under the "count" key is never displayed in console, yet it is passed to the hashtable somehow.
And, One more point to be noted. If I use:
$List2 = @{"First" = $tempHash.First; "Second"= $tempHash.Second}
then, I'll have to use:
$List2.First.Value #to get the value of the "First" key
and that makes me feel uncomfortable because for the $List1
hashtable, I only needed to use:
$List1.First #to get the value of the "First" key.
[Workaround]
I created a hastable $List
as the original hashtable as below for strictly one time use only :
$List | ConvertTo-Json | Set-Content -Path $jsonFile
Then, I created two hastables $List1
and $List2
as below from the original $jsonFile
above.
$tempHash = Get-Content -Path $jsonFile -Raw| ConvertFrom-Json
$List1 = @{"First" = $tempHash.First; "Second" = tempHash.Second}
$List2 = @{"First" = $tempHash.First; "Second" = tempHash.Second}
It helped me keep consistency while referring to their keys and values.
Now, I use
#to fetch the values of the "First" keys of both hashtables.
$List1.First.value #and
$List2.First.value
Similarly, I do the same for "Second" key for both hashtables $List1
and $List2
.
#to fetch the values of the "Second" keys of both hashtables.
$List1.Second.value #and
$List2.Second.value
[Edit]
It turned out to be a bug in my version of Powershell as ststed by @mklement0 below. The perfect solution will be to instead use the syntax below as instructed by @mklement0 :
# Ensure that the input array is constructed without the extra [psobject] wrapper.
$First = [Byte[]]::new(32)
$Second = [Byte[]]::new(32)
The result of the first ConvertTo-Json
call is a quirk in Windows PowerShell as of v5.1: the resulting JSON should have First
and Second
contain an array directly rather than an object with value
and Count
properties, with value
containing the array.
$a = New-Object Byte[] 2; @{ a = $a } | ConvertTo-Json -Compress
$a = New-Object Byte[] 2; @{ a = $a } | ConvertTo-Json -Compress
yields:
{"a":[0,0]}
in PowerShell Core v6.0.1 - OK. {"a":{"value":[0,0],"Count":2}}
in Windows PowerShell v5.1 - BROKEN. In your case it is the use of New-Object
that triggers the quirk .
Workaround :
At the start of your script/session, run:
Remove-TypeData System.Array
This removes the obsolete ETS-supplied .Count
property from all array objects, which makes the problem go away for [psobject]
-wrapped objects (such as returned by New-Object
) - for an explanation, see this answer of mine.
More cumbersome workarounds :
The problem goes away if you ensure that -is [psobject]
no longer reports true for the input arrays , which can be done in one of the following ways:
[PSv5+]: $First = [Byte[]]::new(32)
- use of an expression rather than a command makes the problem go away, because it doesn't create an extra, invisible [psobject]
wrapper.
[PSv4-]: $First = (New-Object Byte[] 32).psobject.BaseObject
- explicitly bypassing the extra [psobject]
wrapper makes the problem go away.
Simplified example (PSv5+, but easily adapted to earlier versions; file operations omitted, because they are incidental to the problem):
# Ensure that the input array is constructed without the extra [psobject] wrapper.
$First = [Byte[]]::new(2)
# Construct a hashtable...
$List1 = @{ First = $first }
# ... and convert it to JSON:
($json = $List1 | ConvertTo-Json)
The above now correctly yields ( no extraneous object with value
and count
properties):
{
"First": [
0,
0
]
}
Reconverting this JSON string to an object now works as expected:
# Re-convert: $tempObj.First then *directly* contains the input array
# (there is no .Value property anymore).
$tempObj = $json | ConvertFrom-Json
# Construct a new hashtable...
$List2 = @{ First = $tempObj.First }
# ... and convert it to JSON.
$List2 | ConvertTo-Json
The result is the same JSON string as above.
Change this:
$List2 = @{"First" = $tempHash.First.value; "Second"= $tempHash.Second.value}
To this:
$List2 = @{"First" = $tempHash.First; "Second"= $tempHash.Second}
This will maintain the value
and count
structures. On testing, it's also kept the order on the values in the value
section.
Edit
To enhance your workaround, you can put in this step so that you can access the values without having to use .value
:
$firstValues = [byte[]]($tempHash.First.value)
$secondValues = [byte[]]($tempHash.Second.value)
$List1 = @{"First" = $firstValues; "Second" = $secondValues}
$List2 = @{"First" = $firstValues; "Second" = $secondValues}
Self-Contained Test Script
$jsonFile1 = "jsonFile1.json"
$jsonFile2 = "jsonFile2.json"
$first = New-Object Byte[] 32
$second = New-Object Byte[] 16
foreach($i in 0..($first.COunt -1)){
$first[$i] = Get-random -minimum 1 -maximum 254
}
foreach($i in 0..($second.COunt -1)){
$second[$i] = Get-random -minimum 1 -maximum 254
}
$List = @{"First" = $first; "Second" = $second}
$List | ConvertTo-Json | Set-Content -Path $jsonFile1
$tempHash = Get-Content -Path $jsonFile1 -Raw | ConvertFrom-Json
$firstValues = [byte[]]($tempHash.First.value)
$secondValues = [byte[]]($tempHash.Second.value)
$List1 = @{"First" = $firstValues; "Second" = $secondValues}
$List2 = @{"First" = $firstValues; "Second" = $secondValues}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.