powershell convert-tojson not giving optimal results - powershell

I have a powershell question, I need to convert this piece of code to Json
$Dimensions = #(
#{
name = 'num_vm'
value = '100'
},
#{
name = 'days'
value = '10'
},
#{
name = 'months'
value = '12'
}
)
For ($i=0;$i -lt $Dimensions.length;$i++)
{
$bodymessage = #{'resourceId' = $resourceUsageId; 'quantity' = "$Dimensions[$i].value"; 'dimension' = "$Dimensions[$i].name"; 'effectiveStartTime' = $lastHourMinusFiveMinutes; 'planId' = 'plan_meter' }
$body += #{
request =
#($bodymessage)} `
|ConvertTo-Json -Depth 5
}
$body
what I'm hoping, is it can dynamically create the JSON output according to the dimension input,
but the result I got is this
{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].name",
"effectiveStartTime": null
}
]
}{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[1].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[1].name",
"effectiveStartTime": null
}
]
}{
"request": [
{
"resourceId": null,
"quantity": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[2].value",
"planId": "plan_meter",
"dimension": "System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[2].name",
"effectiveStartTime": null
}
]
}
how can i get rid of System.Collections.Hashtable System.Collections.Hashtable System.Collections.Hashtable[0].value?
and displaying with real value?

The main issue is how you're assigning the hashtable element values within a string. Fix it by removing the quotes like so (formatted multiline for readability):
$bodymessage = #{
resourceId = $resourceUsageId;
quantity = $Dimensions[$i].value; # Removed the quotes here
dimension = $Dimensions[$i].name; # Removed the quotes here
effectiveStartTime = $lastHourMinusFiveMinutes;
planId = 'plan_meter';
}
Note: I've also removed the single-quotes ' around the key names since they are not needed in this context. You only need to quote key names if non-alphanumeric characters are part of the key name, and you are assigning the key name as a literal string.
Why did this happen?
This happens because of how variable expansion within strings works. The following string you originally had:
"$Dimensions[$i].value"
will see $Dimensions get expanded (its ToString() function is implicitly called), not $Dimensions[$i].value. This is because variable parsing starts at the dollar-sign $ and stops at the first character which can't normally be part of a variable name. This will be most special characters, excepting the colon :.
However, if you are trying to force a string value (and you may not be), you can perform a sub-expression within the string to evaluate the members of an object, including array-accessors and object properties. You can also perform more complex expressions as well, but for your case below you could leave the quotes and use $() to get the result you want:
"$($Dimensions[$i].value)"
The $() tells the parser to "run this expression and put the result into the string". Again, whatever object is returned will implicitly have ToString() run on it when it gets inserted into the final string, so whatever output $Dimensions[$i].value.ToString() produces will be your string value.

Related

A dictionary inside of a dictionary in PowerShell

So, I'm rather new to PowerShell and just can't figure out how to use the arrays/lists/hashtables. I basically want to do the following portrayed by Python:
entries = {
'one' : {
'id': '1',
'text': 'ok'
},
'two' : {
'id': '2',
'text': 'no'
}
}
for entry in entries:
print(entries[entry]['id'])
Output:
1
2
But how does this work in PowerShell? I've tried the following:
$entries = #{
one = #{
id = "1";
text = "ok"
};
two = #{
id = "2";
text = "no"
}
}
And now I can't figure out how to access the information.
foreach ($entry in $entries) {
Write-Host $entries[$entry]['id']
}
=> Error
PowerShell prevents implicit iteration over dictionaries to avoid accidental "unrolling".
You can work around this and loop through the contained key-value pairs by calling GetEnumerator() explicitly:
foreach($kvp in $entries.GetEnumerator()){
Write-Host $kvp.Value['id']
}
For something closer to the python example, you can also extract the key values and iterate over those:
foreach($key in $entries.get_Keys()){
Write-Host $entries[$key]['id']
}
Note: You'll find that iterating over $entries.Keys works too, but I strongly recommend never using that, because PowerShell resolves dictionary keys via property access, so you'll get unexpected behavior if the dictionary contains an entry with the key "Keys":
$entries = #{
Keys = 'a','b'
a = 'discoverable'
b = 'also discoverable'
c = 'you will never find me'
}
foreach($key in $entries.Keys){ # suddenly resolves to just `'a', 'b'`
Write-Host $entries[$key]
}
You'll see only the output:
discoverable
also discoverable
Not the Keys or c entries
To complement Mathias R. Jessen's helpful answer with a more concise alternative that takes advantage of member-access enumeration:
# Implicitly loops over all entry values and from each
# gets the 'Id' entry value from the nested hashtable.
$entries.Values.Id # -> 2, 1
Note: As with .Keys vs. .get_Keys(), you may choose to routinely use .get_Values() instead of .Values to avoid problems with keys literally named Values.

Powershell Append PSCustomObject to nested PSCustomObject

My problem is that I have a remote API giving me a JSON with 10 levels deep nesting and I need to add another array/hashtable/PSCustomObject (whatever you want to call it) to one of them.
The JSON looks like this:
{
"result": [
"modules": [
"data": {
"segments": [
{
"routes": {
"static": [
{
"destination": "10.0.0.0",
"netmask": "255.0.0.0"
},
{
"destination": "172.16.0.0",
"netmask": "255.240.0.0"
}
]
}
}
]
}
]
]
}
The return object is a Hashtable, however when I try to access the values in dot notation, it turns into PSCustomObject:
$statics = $result.result.modules.data.segments.routes.static
It won't let me use the other notation:
$statics = $result['result']['modules']['data']['segments']['routes']['static']
Error: Cannot index into a null array
The real tricky part (for me) is to to append a new hastable to the "static" hashtable in such a way that the rest of the hashtable remains intact.
$newroute = #{
destination = "1.1.1.1."
netmask = "255.255.255.255"
}
I would have used PHP or Python but for this project I must use Powershell and I'm running into all sorts of things where PS behaves different from what I expect.
Any help is greatly appreciated!
static is an array, you can add new items to it:
$newStaticRoute = [pscustomobject]#{
destination = '192.168.0.0'
netmask = '255.255.0.0'
}
$result.result.modules.data.segments.routes.static += $newStaticRoute

PowerShell object properties recursive

I need a function with the following requirements:
The function is assigned an unknown object. It is not known how many properties and / or sub-objects the object has.
The function then iterates recursively through the object until it knows all the properties of the object.
PowerShell should then output this like a JSON file. But without the added characters ( { } [ ] , ).
I'm pretty sure the ConvertTo-Json cmdlet has such a function, but I can't script / use it myself.
Please format the whole thing with the cmdlet "Format-List" or something like that and not manually with tabs or spaces.
Here is a small example:
Example object:
$Car = [PSCustomObject] #{
Tire = [PSCustomObject] #{
Color = "Black"
Count = 4
}
Doors = [PSCustomObject]#{
Color = "Blue"
Diameter = 21
}
}
Output of a JSON file:
{
"Tire": {
"Color": "Black",
"Count": 4
},
"Doors": {
"Color": "Blue",
"Diameter": 21
}
}
Required output:
Tire:
Color: Black
Count: 4
Doors:
Color: Blue
Diameter: 21
The output should be saved in a string variable.
I've been researching for days, but can't find anything.
Thanks you so much.
The output you are looking for above appears to be (at least close to) YAML syntax. You could use the 'powershell-yaml' module to output in the form that you need.
Make sure you have installed the 'powershell-yaml' module (Install-Module powershell-yaml) and then try to following:
Command
$Car = [PSCustomObject] #{
Tire = [PSCustomObject] #{
Color = "Black"
Count = 4
}
Doors = [PSCustomObject]#{
Color = "Blue"
Diameter = 21
}
}
ConvertTo-Yaml $car
Output
Tire:
Color: Black
Count: 4
Doors:
Color: Blue
Diameter: 21
The output is the same as what you are looking for and ConvertTo-Yaml should work for all PSObjects.

Retrieve value from Json object with field having dots and hyphen in powershell

I want to retrieve value of a field present in json object. The filed name has dots and hyphen.
For eg:
$json = #"
{
"Stuffs":
{
"Name.new-name": "Darts",
"Type": "Fun Stuff"
}
}
"#
How can I get the value Darts?
I tried some approaches like
$x = $json | ConvertFrom-Json
$x.Stuffs.(Name.new-name)
But it doesn't work.
Try this
$x.Stuffs.'Name.new-name'

PowerShell PSObject change field names before converted to JSON object

I have a project in PowerShell where I created a custom type using the "Add-Type". I wanted to use classes, but I'm limited to the PowerShell version on the system I have to use.
This object gets converted to JSON and is sent out using a REST API. To avoid some naming issues I ended up pre-pending ""s to the field names. I want to remove those before it is sent to the API. I thought it would be simple to do the rename, and it was, but then I realized the method I used for the rename would potentially cause issues if I renamed everything in the JSON object by removing anything with the "".
Here is the custom type:
Add-Type -typedef #"
public class SearchFilter
{
public int _filterOrder;
public string _property;
public string _direction;
public string _value;
public string[] _values;
public string _operator;
public SearchFilter()
{
_filterOrder = 0;
_property = "";
_direction = "";
_value = "";
_values = new string[0];
_operator = "";
}
}
"#
Using just a string REPLACE method I'll get this:
{
"filterOrder": 0,
"property": "name",
"direction": "ASC",
"value": "value",
"values": [],
"operator": "Contains"
}
instead of this:
{
"filterOrder": 0,
"property": "name",
"direction": "ASC",
"value": "_value_",
"values": [],
"operator": "Contains"
}
Here is the method I'm using:
# Create search filter object
$searchFilter = New-Object -TypeName "SearchFilter"
# Convert search filter to REST API JSON format
$apiPostBody = ConvertTo-Json -InputObject $searchFilter
# Remove all "_"
$apiPostBody = $apiPostBody .Replace("_","")
I have checked the methods available from PSObject, but I'm just not sure of the syntax. If possible I'd like to change the existing object before converting to JSON as opposed to having to create a CLONE or iterate through the JSON value.