I am quite new to powershell stuff. So need some help here.
I want to pass a json object as a parameter to another ps1. From what I read after searching is that I need to convert it to powershell object from json string. Please correct me if I am wrong. This is what I am doing
Calling script:
$jsonParams = "{
`"TaskName`": `"$taskName`",
`"ExitCode`": `"$exitCode`",
`"ErrorMessage`": `"$errorMessage`"
}
$jsonObject = $jsonParams | ConvertFrom-Json
$argumentList = #($param1, $param2, $jsonObject)
Invoke-Expression "& `"$scriptPath`" $argumentList"
and in called script -
param (
[string]$param1,
[string]$param2,
[Microsoft.PowerShell.Commands.JsonObject]$jsonObject
)
But, the calling script throws error
ConvertFrom-Json : Invalid object passed in, ':' or '}' expected. (21): {
What's wrong with this code. Also, after json object is passed to called script, how should I access its values in it.
Thanks!!
Your JSON is malformed. I think the core issue is that you have a trailing comma at the end of your JSON. You also don't close the opening quotation in your declaration.
You might have a much easier time if you use a here-string for this anyway. This was you don't have to use all those backticks.
$jsonParams = #"
{
"TaskName": "$taskName",
"ExitCode": "$exitCode",
"ErrorMessage": "$errorMessage"
}
"#
$jsonObject = $jsonParams | ConvertFrom-Json
$jsonObject is already a custom object and no longer JSON. You don't need to do anything special with it. Remove the type in your param block and just call the properties in your script.
param (
[string]$param1,
[string]$param2,
$jsonObject
)
$jsonObject.TaskName
Related
I have a powershell based http trigger azure function that uses the Az module to call get-azvm. It get's it's data from a logic-app POST. The output looks correct and it's type shows as string but the cmdlet does not like the variable. The function looks like this:
Write-Host "PowerShell HTTP trigger function processed a request."
$sub = $Request.Body.subject | Out-String
write-host "sub:" $sub
$split = $sub -split "[/]"
write-host "split:" $split
$avd = $split[8] | Out-string
Write-Host "avd" $avd
$rgName_avd = 'rg-azgroup'
Get-AzVM -Name $avd -ResourceGroupName $rgName_avd
The post input looks like:
/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourcegroups/rg-azgroup/providers/Microsoft.Compute/virtualMachines/myvmname-0
The error in the logs starts as:
[Error] ERROR: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.Exception :Type : Newtonsoft.Json.JsonReaderExceptionTargetSite :Name : ParseValueDeclaringType : Newtonsoft.Json.JsonTextReaderMemberType
It feels like it's an issue with the input type but using ConvertFrom-Json does not seem to work. Any ideas what I'm doing wrong? If I hard code the VM name or define a variable locally and use it the command executes.
The AZ module versions being used in requirements.psd1:
#{
'Az.Accounts' = '2.7.6'
'Az.Compute' = '4.26.0'
}
Here are the few workarounds that you can try:
Solution 1:
You might not be passing JSON to DeserializeObject.
It looks like that type of tmpfile from File.WriteAllText(tmpfile,... is string that contain file path. JsonConvert.DeserializeObject takes JSON value rather than the file path, it fails trying to convert something like #"c:\temp\fooo" - which is not JSON.
Solution 2:
Check that is the file containing JSON string has BOM.
Once u remove BOM the problem may get resolved.
Solution 3:
For a Web API action that was binding to a string instead to an object or a JObject when JSON was correct, but the binder tried to obtain a string from the JSON structure and failed.
So, instead of:
[HttpPost("[action]")]
public object Search([FromBody] string data)
Try this:
[HttpPost("[action]")]
public object Search([FromBody] JObject data)
References:
Unexpected character encountered while parsing value
Unexpected character encountered while parsing value ASP.NET Core and Newtonsoft
I have an automation runbook in azure that receives a json string in a variable like so:
["value1","value2"]
The problem is that I can't figure out how to handle this variable and parse it.
$array = "'$jsonInput'"
$Services = $array | ConvertFrom-Json
switch($Services)
{
value1 { $requested = $true ; break }
Default { $requested = $false }
}
Using only single quotes does not resolve my input variable. Using "'$jsonInput'" does nothing and the variable $requested returns false. I believe this is because of the use of double quotes in the input variable. What is the correct way to parse this?
If I manually enter this when testing, it works.
$array = '["value1","value2"]'
But then again, not sure how to take this json string without quotes, append the quotes and resolve the variable.
I am trying to pass pass parameter with space to aws cloudformation create-stack aws cli.
The issue is that my parameter has space. I am using powershell for scripting.
Below is example of my parameter
$JsonParameter = '[{"ParameterKey":"name","ParameterValue":"John"},{"ParameterKey":"Occupation","ParameterValue":"Test Engineer"}]'| ConvertTo-Json
This returns
"[{\"ParameterKey\":\"name\",\"ParameterValue\":\"John\"},{\"ParameterKey\":\"Occupation\",\"ParameterValue\":\"Test Engineer\"}]"
cli command is
aws cloudformation create-stack --stack-name $stackName --template-url $templateUrl --capabilities $capabilityList --parameters $JsonParameter --region "us-east-1"
The error goes
Error parsing parameter '--parameters': Invalid JSON:
[{"ParameterKey":"name","ParameterValue":"John"},{"ParameterKey":"Occupation","ParameterValue":"Test
From the error, it looks like cli doesn't like the space in the ParameterValue.
How do I escape the space, so that cli doesn't complain about the space in the value?
Remove | ConvertTo-Json.
Your string is already a json string so you do not want to perform a conversion.
$JsonParameter = '[{"ParameterKey":"name","ParameterValue":"John"},{"ParameterKey":"Occupation","ParameterValue":"Test Engineer"}]'
Just use the string as is.
Alternate scenario
Should you be working with a Powershell object rather than a json string, you might want at some point to convert it into a json to pass it as parameter to your aws call.
That's the moment where ConvertTo-Json would reveal itself to be useful.
Take this for instance
$JsonParameter = #(
#{
ParameterKey = 'name'
ParameterValue = 'John'
},
#{
ParameterKey = 'Occupation'
ParameterValue = 'Test Engineer'
}
)
This is a Powershell object, for which you might had, in a different context, built from scratch with the intent of passing it as a json paramerter to your aws call.
Now, to achieve the transition from this state of "array of hashtables" to a valid json string, you need to use the ConvertTo-Json cmdlet.
$JsonParameterString = $JsonParameter | Convertto-json -Compress
The resulting string he same as you had initially, ready to be passed down to aws :
[{"ParameterKey":"name","ParameterValue":"John"},{"ParameterKey":"Occupation","ParameterValue":"Test Engineer"}]
If on the other hand, you had a json string and needed to edit it without fuss, you could use the ConvertFrom-Json cmdlet, then edit the resulting object as needed and convert it back to json again before passing it down.
Additional note
In my Powershell to Json example,
I used the -compress switch parameter. This is optional. This will create a compressed json string (one line) instead of an expanded one.
Reference
Powershell doc - ConvertTo-Json
First off, I am bound to PowerShell v2 because that is what is installed by default on Windows 7. What I want to do works out of the box with PowerShell v4, and, possibly, PowerShell v3.
I want to read a JSON object from a file and use it as an object in my script. PowerShell v2 does not have the ConvertFrom-Json method, so I wrote my own implementation that I want to have the same functionality:
function ConvertFrom-Json
{
param(
[Parameter(ValueFromPipeline=$true)]
[string]$json
)
[System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
$ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$obj = $ser.DeserializeObject($json)
Write-Host $obj.GetType()
return $obj
}
I call this function like this:
$configObj = (Get-Content $configFile) -join "`n" | ConvertFrom-Json
Write-Host $configObj.GetType()
The output of this:
System.Collections.Generic.Dictionary`2[System.String,System.Object]
System.Object[]
And I am not able to interact with $configObj like an object and get it's properties. In fact, I can't figure out how to get any information out of it. When I print the object, the output looks like:
AC Version Location
--- ------- --------
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.Web.Extensions\3.5.0.0__31bf3856ad364e35\System.Web.Extensions.dll
Key : DownloadURL
Value : https://example.com
Key : dir
Value :
When I expect it to look like:
Key Value
--- -----
DownloadURL https://example.com
dir
Is there a way to work around this and get the same object back from my ConvertFrom-Json method as exists inside my method?
Thanks in advance,
Andy
Result of every statement, with exception for assignment and increment/decrement, considered part of your function return. You does not have to use return statement to return something from function. So your function actually return two objects: loaded Assembly object from LoadWithPartialName method and dictionary from $obj variable. When you assign multiple function results to variable, PowerShell have to pack results into array. As you does not actually need Assembly object, you can cast result of LoadWithPartialName method to [void]:
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
Also LoadWithPartialName method is Obsolete and you should not use it at all. For loading assembly, you can use Add-Type build-in cmdlet:
Add-Type -AssemblyName System.Web.Extensions
As you does not use named blocks in your function, all code considered to be an end block, thus you only process last pipeline input in your function. You should use process block to process every pipeline input object:
process{
$ser.DeserializeObject($json)
}
When statement return collection, than PowerShell enumerate this collection and write each individual collection's item instead of collection as single element. So if your JSON contains array at top level, you function will return array elements instead of array itself, as build-in ConvertFrom-Json does. You can use unary array operator to prevent enumeration of returned array:
,$ser.DeserializeObject($json)
The total:
function ConvertFrom-Json {
param(
[Parameter(ValueFromPipeline=$true)]
[string]$json
)
begin {
Add-Type -AssemblyName System.Web.Extensions
$ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
}
process {
,$ser.DeserializeObject($json)
}
}
I am using PowerShell v3 and the Windows PowerShell ISE. I have the following function that works fine:
function Get-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# If a Namespace URI was not given, use the Xml document's default namespace.
if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }
# In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
[System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
$xmlNsManager.AddNamespace("ns", $NamespaceURI)
[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter
# Try and get the node, then return it. Returns $null if the node was not found.
$node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
return $node
}
Now, I will be creating a few similar functions, so I want to break the first 3 lines out into a new function so that I don't have to copy-paste them everywhere, so I have done this:
function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
# If a Namespace URI was not given, use the Xml document's default namespace.
if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }
# In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
[System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
$xmlNsManager.AddNamespace("ns", $NamespaceURI)
return $xmlNsManager
}
function Get-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
[System.Xml.XmlNamespaceManager]$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter
# Try and get the node, then return it. Returns $null if the node was not found.
$node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
return $node
}
The problem is that when "return $xmlNsManager" executes the following error is thrown:
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Xml.XmlNamespaceManager".
So even though I have explicitly cast my $xmlNsManager variables to be of type System.Xml.XmlNamespaceManager, when it gets returned from the Get-XmlNamespaceManager function PowerShell is converting it to an Object array.
If I don't explicitly cast the value returned from the Get-XmlNamespaceManager function to System.Xml.XmlNamespaceManager, then the following error is thrown from the .SelectSingleNode() function because the wrong data type is being passed into the function's 2nd parameter.
Cannot find an overload for "SelectSingleNode" and the argument count: "2".
So for some reason PowerShell is not maintaining the data type of the return variable. I would really like to get this working from a function so that I don't have to copy-paste those 3 lines all over the place. Any suggestions are appreciated. Thanks.
What's happening is PowerShell is converting your namespace manager object to a string array.
I think it has to do with PowerShell's nature of "unrolling" collections when sending objects down the pipeline. I think PowerShell will do this for any type implementing IEnumerable (has a GetEnumerator method).
As a work around you can use the comma trick to prevent this behavior and send the object as a whole collection.
function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
...
$xmlNsManager.AddNamespace("ns", $NamespaceURI)
return ,$xmlNsManager
}
More specifically, what is happening here is that your coding habit of strongly typing $fullyQualifiedModePath is trying to turn the result of the Get (which is a list of objects) into a string.
[string]$foo
will constrain the variable $foo to only be a string, no matter what came back. In this case, your type constraint is what is subtly screwing up the return and making it Object[]
Also, looking at your code, I would personally recommend you use Select-Xml (built into V2 and later), rather than do a lot of hand-coded XML unrolling. You can do namespace queries in Select-Xml with -Namespace #{x="..."}.