PowerShell ConvertFrom-Json conversion error - json

My PowerShell ConvertFrom-Json for the below throws the "Invalid JSON primitive" error.
{
"ScriptEndTime": "19 April 2022 10:26:32",
"AbortReason": "Server Offline",
"Server": "Server3",
"ScriptStartTime": "19 April 2022 10:25:55",
"Notes": "",
"NumberOfReboots": 0,
"Abort": true,
"UserAccount": "Contoso\\ContosoAdmin"
} {
"OS": "Microsoft Windows Server 2012 R2 Standard",
"Abort": false,
"KB5011560": "Succeeded",
"Server": "Server4.contoso.co.uk",
"IPAddress": "3.3.3.3"
}
I even tried to validate the above using jsonlint.com and it shows Expecting 'EOF', '}', ',', ']', got '{' error
What am I missing? Thanks.
Update: 20/04/2022
Below is what was happening internally in my case:
$MyHt1 = #{}
$MyHt1.Server = "Server1"
$MyHt1.StartTime = '1/1/2021 09:30:30 AM'
$MyHt1."" = "Succeeded" #my script was setting empty key here which is the source of the issue
$MyHt2 = #{}
$MyHt2.Server = "Server2"
$MyHt2.StartTime = '2/2/2022 09:30:30 PM'
$MyHt2."KB54231" = "Pending"
$JsonOutputArray = #($MyHt1, $MyHt2 | Foreach-Object { ConvertTo-Json -InputObject $_ })
$JsonOutputArray | Out-String | ConvertFrom-Json #throws runtime error here due to empty key
Looks like PowerShell hash table allows an empty key. That was the root cause of this issue which later resulted in runtime error during json conversion. Interestingly, ConvertTo-Json allows empty hash table key but fails with ConvertFrom-Json

Those are two separate JSON files to add them in one JSON file you should ad them in one bracket {} and the other items are separated by ","
as the following :
{
name1:{
"ScriptEndTime": "19 April 2022 10:26:32",
"AbortReason": "Server Offline",
"Server": "Server3",
"ScriptStartTime": "19 April 2022 10:25:55",
"Notes": "",
"NumberOfReboots": 0,
"Abort": true,
"UserAccount": "Contoso\\ContosoAdmin"
},
name2:{
"OS": "Microsoft Windows Server 2012 R2 Standard",
"Abort": false,
"KB5011560": "Succeeded",
"Server": "Server4.contoso.co.uk",
"IPAddress": "3.3.3.3"
}
}

Related

How to update json key fields in powershell script [duplicate]

Folks,
I do run in PowerShell some ReST api call and do get back a
$response
page content
---- -------
#{size=20; number=0; totalPages=1; totalElements=1} {#{id=ZGR2ZS0yLnZsYWIubG9j…
When I convert to JSON
PS C:\Users\Administrator\Documents> $response | ConvertTo-Json
{
"page": {
"size": 20,
"number": 0,
"totalPages": 1,
"totalElements": 1
},
"content": [
{
"id": "ZGR2ZS0yLnZsYWIubG9jYWw6MzAwOTpob3N0",
"host": "ddve-2.vlab.local",
"port": "3009",
"notValidBefore": "Fri Mar 29 21:32:19 PDT 2019",
"notValidAfter": "Sat Mar 29 04:32:19 PDT 2025",
"fingerprint": "E1BB40B0284595297071177FE02BC9C76E85CD66",
"subjectName": "CN=ddve-2.vlab.local, O=Valued DataDomain customer, OU=Hos
t Certificate, ST=CA, C=US",
"issuerName": "CN=ddve-2.vlab.local, OU=Root CA, O=Valued Datadomain Custo
mer, L=Santa Clara, ST=CA, C=US",
"state": "ACCEPTED",
"type": "HOST"
}
]
}
an idividual value can be seen
PS C:\Users\Administrator\Documents> $response.content.state
ACCEPTED
But setting a new value fails
PS C:\Users\Administrator\Documents> $response.content.state = 123
InvalidOperation: The property 'state' cannot be found on this object. Verify th
at the property exists and can be set.
Because your content property is a (single-element) array (as evidenced by its value being enclosed in [ ... ] in the JSON representation), you must use an index to specify which element's .state property to set:
$response.content[0].state = 123
Note that an index is not required for getting the value (that is, $response.content.state works and returns "ACCEPTED", as you state), because PowerShell then applies member-access enumeration, which means that it enumerates the state property values of all elements of the array (which with a single-element array returns just the single element's value, and with a multiple-element array returns an array of values).
On setting a property value, member-access enumeration is by design not supported, although the error message could certainly be more helpful - see this answer.
Your object is a PSCustomObject and the value you're trying to change is a NoteProperty on that object, you can use Add-Member with the -Force switch to overwrite it.
$Response = #"
{
"page": {
"size": 20,
"number": 0,
"totalPages": 1,
"totalElements": 1
},
"content": [
{
"id": "ZGR2ZS0yLnZsYWIubG9jYWw6MzAwOTpob3N0",
"host": "ddve-2.vlab.local",
"port": "3009",
"notValidBefore": "Fri Mar 29 21:32:19 PDT 2019",
"notValidAfter": "Sat Mar 29 04:32:19 PDT 2025",
"fingerprint": "E1BB40B0284595297071177FE02BC9C76E85CD66",
"subjectName": "CN=ddve-2.vlab.local, O=Valued DataDomain customer, OU=Hos
t Certificate, ST=CA, C=US",
"issuerName": "CN=ddve-2.vlab.local, OU=Root CA, O=Valued Datadomain Custo
mer, L=Santa Clara, ST=CA, C=US",
"state": "ACCEPTED",
"type": "HOST"
}
]
}
"# | ConvertFrom-Json
$Response.Content.State
$Response.Content | Add-Member -MemberType NoteProperty -Name State -Value 123 -Force
$Response.Content.State
Output
ACCEPTED
123

PowerShell JSON set value

Folks,
I do run in PowerShell some ReST api call and do get back a
$response
page content
---- -------
#{size=20; number=0; totalPages=1; totalElements=1} {#{id=ZGR2ZS0yLnZsYWIubG9j…
When I convert to JSON
PS C:\Users\Administrator\Documents> $response | ConvertTo-Json
{
"page": {
"size": 20,
"number": 0,
"totalPages": 1,
"totalElements": 1
},
"content": [
{
"id": "ZGR2ZS0yLnZsYWIubG9jYWw6MzAwOTpob3N0",
"host": "ddve-2.vlab.local",
"port": "3009",
"notValidBefore": "Fri Mar 29 21:32:19 PDT 2019",
"notValidAfter": "Sat Mar 29 04:32:19 PDT 2025",
"fingerprint": "E1BB40B0284595297071177FE02BC9C76E85CD66",
"subjectName": "CN=ddve-2.vlab.local, O=Valued DataDomain customer, OU=Hos
t Certificate, ST=CA, C=US",
"issuerName": "CN=ddve-2.vlab.local, OU=Root CA, O=Valued Datadomain Custo
mer, L=Santa Clara, ST=CA, C=US",
"state": "ACCEPTED",
"type": "HOST"
}
]
}
an idividual value can be seen
PS C:\Users\Administrator\Documents> $response.content.state
ACCEPTED
But setting a new value fails
PS C:\Users\Administrator\Documents> $response.content.state = 123
InvalidOperation: The property 'state' cannot be found on this object. Verify th
at the property exists and can be set.
Because your content property is a (single-element) array (as evidenced by its value being enclosed in [ ... ] in the JSON representation), you must use an index to specify which element's .state property to set:
$response.content[0].state = 123
Note that an index is not required for getting the value (that is, $response.content.state works and returns "ACCEPTED", as you state), because PowerShell then applies member-access enumeration, which means that it enumerates the state property values of all elements of the array (which with a single-element array returns just the single element's value, and with a multiple-element array returns an array of values).
On setting a property value, member-access enumeration is by design not supported, although the error message could certainly be more helpful - see this answer.
Your object is a PSCustomObject and the value you're trying to change is a NoteProperty on that object, you can use Add-Member with the -Force switch to overwrite it.
$Response = #"
{
"page": {
"size": 20,
"number": 0,
"totalPages": 1,
"totalElements": 1
},
"content": [
{
"id": "ZGR2ZS0yLnZsYWIubG9jYWw6MzAwOTpob3N0",
"host": "ddve-2.vlab.local",
"port": "3009",
"notValidBefore": "Fri Mar 29 21:32:19 PDT 2019",
"notValidAfter": "Sat Mar 29 04:32:19 PDT 2025",
"fingerprint": "E1BB40B0284595297071177FE02BC9C76E85CD66",
"subjectName": "CN=ddve-2.vlab.local, O=Valued DataDomain customer, OU=Hos
t Certificate, ST=CA, C=US",
"issuerName": "CN=ddve-2.vlab.local, OU=Root CA, O=Valued Datadomain Custo
mer, L=Santa Clara, ST=CA, C=US",
"state": "ACCEPTED",
"type": "HOST"
}
]
}
"# | ConvertFrom-Json
$Response.Content.State
$Response.Content | Add-Member -MemberType NoteProperty -Name State -Value 123 -Force
$Response.Content.State
Output
ACCEPTED
123

Webscraping Local web application, Trying to update the JSON object with Powershell

I am working on a project. I have a JSON object that has been pulled from a web scrape. The web is a html version of something I am working on to see current movies and if it is added to my watchedlist for IMDB. :
{
"title": "The Gallows Act II",
"alternativeTitles": [],
"secondaryYearSourceId": 0,
"sortTitle": "gallows act ii",
"status": "released",
"overview": "When Ana Rue transfers to a prestigious
new acting school, she encounters a malevolent spirit after participating in a viral challenge.",
"inCinemas": "2019-10-25T05:00:00Z",
"physicalRelease": "2019-10-25T00:00:00Z",
"physicalReleaseNote": "",
"images": [
{
"coverType": "poster",
"url": "/MediaCover/54956/poster.jpg"
},
{
"coverType": "fanart",
"url": "/MediaCover/54956/fanart.jpg"
}
],
"website": "",
"year": 2019,
"studio": "Tremendum Pictures",
"profileId": 1,
"pathState": "dynamic",
"monitored": true,
"minimumAvailability": "inCinemas",
"isAvailable": true,
"runtime": 99,
"lastInfoSync": "2020-01-28T16:40:11.982967Z",
"cleanTitle": "thegallowsactii",
"imdbId": "tt7473716",
"tmdbId": 505060,
"titleSlug": "the-gallows-act-ii-505060",
"genres": [],
"tags": [],
"ratings": {
"votes": 12,
"value": 5.3
},
$address="http://localhost:PORT"
$movie_id = ""
#$movie_title = Read-Host "Movie Title"
$params = #{"name"="RescanMovie";"movieId"="$movie_id";} | ConvertTo-Json
$apikey = 'APIKEY FOR APPLICATION'
$converted = Invoke-WebRequest -URI "$address/api/movie/" -Method GET/(10) -UseBasicParsing -Header #{"X-Api-Key" = $apikey}
$converted
#$convertto = Invoke-WebRequest -URI "$address/api/movie/" -Method PUT -UseBasicParsing -Header #{"X-Api-Key" = $apikey}
Now I pull this information with API pull : Invoke-Webrequest.
I get the objects.
I want to change the "Monitored" if it is on the list I want to change it to false or true respectively.
I break the object down to initial search "Monitored" is -eq true.
It pulls them in.
I want to pull the JSON information for all fields" update the Monitored status to true or false if its found in the list.
-Then pass all fields back to the json object and updated it.
However. I am not sure how to perform the POST command in powershell to update the whole list passing it back.

Replace and add json within another json

I have a Main json file.
{
"swagger": "2.0",
"paths": {
"/agents/delta": {
"get": {
"description": "lorem ipsum doram",
"operationId": "getagentdelta",
"summary": "GetAgentDelta",
"tags": [
"Agents"
],
"parameters": [
{
"name": "since",
"in": "query",
"description": "Format - date-time (as date-time in RFC3339). The time from which you need changes from. You should use the format emitted by Date's toJSON method (for example, 2017-04-23T18:25:43.511Z). If a timestamp older than a week is passed, a business rule violation will be thrown which will require the client to change the from date. As a best-practice, for a subsequent call to this method, send the timestamp when you <b>started</b> the previous delta call (instead of when you completed processing the response or the max of the lastUpdateOn timestamps of the returned records). This will ensure that you do not miss any changes that occurred while you are processing the response from this method",
"required": true,
"type": "string"
}
]
}
}
}
}
And I have a smaller json file.
{
"name": "Authorization",
"description": "This parameter represents the Authorization token obtained from the OKTA Authorization server. It is the Bearer token provided to authorize the consumer. Usage Authorization : Bearer token",
"in": "header",
"required": true,
"type": "string"
}
Now I need to add the contents of the smaller json file into the Main.Json file in the parameters array.
I tried the below command
cat test.json | jq --argfile sub Sub.json '.paths./agents/delta.get.parameters[ ] += $sub.{}' > test1.json
But I get the below error:
jq: error: syntax error, unexpected '{', expecting FORMAT or QQSTRING_START (Unix shell quoting issues?) at <top-level>, line 1:
.paths += $sub.{}
jq: 1 compile error
cat: write error: Broken pipe
I tried this command.
cat test.json | jq '.paths./agents/delta.get.parameters[ ] | = (.+ [{ "name": "Authorization", "description": "This parameter represents the Authorization token obtained from the OKTA Authorization server. It is the Bearer token provided to authorize the consumer. Usage Authorization : Bearer token", "in": "header", "required": true, "type": "string" }] )' > test1.json
And I get no error and no output either. How do I get around this?
I would have to add the contents of the smaller json file directly first. And then at a later stage, search if it already had name: Authorization and it's other parameters, and then remove and replace the whole name: Authorization piece with the actual contents of the smaller.json, under each path that starts with '/xx/yyy'.
Edited to add:
For the last part of the question, I could not use the walk function, since I have jq 1.5 and since am using the bash task within Azure DevOps, I can't update the jq installation file with the walk function.
Meanwhile I found the use of something similar to wildcard in jq, and was wondering why I can't use it in this way.
jq --slurpfile newval auth.json '.paths | .. | objects | .get.parameters += $newval' test.json > test1.json
Can anyone please point out the issue in the above command? It did not work, and am not sure why..
You want --slurpfile, and you need to escape /agents/delta part of the path with quotes:
$ jq --slurpfile newval insert.json '.paths."/agents/delta".get.parameters += $newval' main.json
{
"swagger": "2.0",
"paths": {
"/agents/delta": {
"get": {
"description": "lorem ipsum doram",
"operationId": "getagentdelta",
"summary": "GetAgentDelta",
"tags": [
"Agents"
],
"parameters": [
{
"name": "since",
"in": "query",
"description": "Format - date-time (as date-time in RFC3339). The time from which you need changes from. You should use the format emitted by Date's toJSON method (for example, 2017-04-23T18:25:43.511Z). If a timestamp older than a week is passed, a business rule violation will be thrown which will require the client to change the from date. As a best-practice, for a subsequent call to this method, send the timestamp when you <b>started</b> the previous delta call (instead of when you completed processing the response or the max of the lastUpdateOn timestamps of the returned records). This will ensure that you do not miss any changes that occurred while you are processing the response from this method",
"required": true,
"type": "string"
},
{
"name": "Authorization",
"description": "This parameter represents the Authorization token obtained from the OKTA Authorization server. It is the Bearer token provided to authorize the consumer. Usage Authorization : Bearer token",
"in": "header",
"required": true,
"type": "string"
}
]
}
}
}
}
And here's one that first removes any existing Authorization objects from the parameters before inserting the new one into every parameters array, and doesn't depend on an the exact path:
jq --slurpfile newval add.json '.paths |= walk(
if type == "object" and has("parameters") then
.parameters |= map(select(.name != "Authorization")) + $newval
else
.
end)' main.json

ConvertFrom-JSON won't accept convertto-json with children when working with WebServiceProxy

I am pulling data from an API using the New-WebServiceProxy in PowerShell 4.0 and then piping it out to a JSON file for review and import on another API service (same API version, etc, just a different host).
$tasklist.Taskconfig | ConvertTo-JSON-Depth 50 -As String | Out-File -FilePath $exportpath\$name.xml -Force
Gives me my XML containing the TaskConfig. In this case, TaskConfig is an object type automatically generated by the API I'm interfacing with. When I want to import the content I am using:
$taskconfig = (Get-Content "$taskjson") -join "`n" | ConvertFrom-Json
but when I run this it's unable to create the object. I assume this is because the JSON contains nested children, giving the error-
Cannot convert value "#{Name=plugindive; Value=;> Children=System.Object[]}" to type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1rcleWeb_WebClientAPI_asmx_wsdl.TaskConfig". Error: "Cannot convert the "#{Name=plugindive; Value=;Children=System.Object[]}" value of type "System.Management.Automation.PSCustomObject" to type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1rcleWeb_WebClientAPI_asmx_wsdl.TaskConfig"."
I've tried explictly stating the type of object:
$taskconfig = [Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1rcleWeb_WebClientAPI_asmx_wsdl.TaskConfig](Get-Content "$taskjson" | Out-string | ConvertFrom-Json)
as well as creating the object then trying to add the children from my JSON -
$taskconfig.children = $json.children
But these all fail in the same way.
I don't seem to get this same issue in PowerShell 5.0 interestingly enough, but I can't verify why - is there another way to approach this?
Added example JSON below
{"Name": "plugindive",
"Value": null,
"Children": [{
"Name": "auto",
"Value": "False",
"Children": [
]
},
{
"Name": "categories",
"Value": null,
"Children": [{
"Name": "Module Z",
"Value": "False",
"Children": [
]
},
{
"Name": "Module A",
"Value": "False",
"Children": [
]
},
{
"Name": "Module B",
"Value": "False",
"Children": [
]
},
{
"Name": "Module C",
"Value": "False",
"Children": [
]
}
]
}
]
}
It seems as if this doesn't work in PowerShell v3.0, so I simply ended up making posts with the explicit XML directly, rather than converting to JSON.