Parse value from JSON when key contains dot (period) in Powershell - json

In the above example, I would like to get the value "Bug" from "System.WorkItemType"
using
$response = Invoke-RestMethod -Url $someUri -Method Get -ContentType "application/json" -Headers $headers
$wit = response.fields.System.WorkItemType
doesn't work since the dot / period messes it up.
In javascript there is this question and answer but did not work in Powershell.
Using regex to replace the dot with underscore seems too farfetched (and I didn't get it to work, but used -match -convertFrom-Json -replace -convertTo-Json so maybe did it wrong?
So my question is simply: How do I get the 'Bug' value from the 'System.WorkItemType' key? (bug may be other strings...)

If you put the property names in "" or '', you should be able to get what you are looking for:
$json= #'
{ "id" : 9983,
"rev" : 17,
"fields" :{
"System.AreaPath":"Cloud\\Dev Blue Team",
"System.TeamProject":"Cloud"
}
}
'#
$j = $Json | convertfrom-json
$j.fields."System.AreaPath"
$J.fields.'System.TeamProject'
if you need to escape double quotes, use the ` grave accent, known as the backtick in PowerShell.
"area path = $($j.fields.`"System.AreaPath`")"

Related

Powershell: Using Invoke-WebRequest, How To Use Variable Within JSON?

Using Powershell, I'm trying to PUT data into an API, but I'm having trouble using a variable within the JSON:
This code below does not generate an error from the API, but it PUTS the singer as, literally, "$var_currentsinger" and doesn't use the intended variable
$currentsinger = "Michael Jackson"
$headers.Add("Content-Type", "application/json")
$response = Invoke-WebRequest -Uri https://stackoverflow.com -Method PUT -Headers $headers -Body '{
"album": {
"name": "Moonlight Sonata",
"custom_fields": [{
"singer": "$currentsinger",
"songwriter": "Etta James"
}]
}
}'
This version below does not work, I assume because there are no quotes around the singer name. The API returns a value that the data is invalid
$currentsinger = "Michael Jackson"
$headers.Add("Content-Type", "application/json")
$response = Invoke-WebRequest -Uri https://stackoverflow.com -Method PUT -Headers $headers -Body '{
"album": {
"name": "Moonlight Sonata",
"custom_fields": [{
"singer": $currentsinger,
"songwriter": "Etta James"
}]
}
}'
The only thing I have tried is double quotes and triple quotes around the variable, but I either get the $currentsinger variable within the JSON and have it submit the variable value and not the variable name.
JSON requires double-quotes, so one example way to handle is by escaping the quotes or with a double-quoted here-string:
# here-string
$json = #"
"singer": $currentsinger,
"songwriter": "Etta James"
"#
# escaped
$json = "
`"singer`": $currentsinger,
`"songwriter`": `"Etta James`"
"

Powershell script Missingtypename

I am trying to make some API calls through powershell using this documentation
https://octopus.com/blog/manually-push-build-information-to-octopus
$Headers = #{"X-Octopus-ApiKey"=$MYKEY"}
$jsonBody = #{
PackageId = "Test"
Version = "1.1.1"
OctopusBuildInformation =
#{
BuildEnvironment = "Jenkins"
BuildNumber = "1"
BuildURL = "myURL"
VcsCommitNumber = "250"
VcsType = "Git"
Commits = [{Id = "250" LinkUrl = "gitUrl" Comment = "someComment"}]
VcsRoot = "gitUrl"
}
} | ConvertTo-Json -Depth 5
Invoke-RestMethod -Method Post -Uri "http://alfadeployment/api/build-information" -Headers $Headers -Body $jsonBody
However, since I added the commits array, I am getting the following error:
Missing type name after '['.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingTypename
PowerShell arrays aren't enclosed in [...] - the latter is used for type literals, i.e. to refer to .NET types - hence the error message.
You may - but don't have to - enclose PowerShell arrays in #(...), the array-subexpression operator; using ,, the array constructor operator is enough, though #(...) can be helpful for visual clarity, and to create an empty (#()) or single-element arrays (#('foo')), which is perhaps clearer than the unary use of , (, 'foo')).
Additionally, the array element in your case must be a hashtable too (#{ ... }, not { ... }, the latter is a script block), and, given that the property definitions are on the same line, they must be ;-separated.
To put it all together:
Commits = #(
#{ Id = "250"; LinkUrl = "gitUrl"; Comment = "someComment" }
)

Powershell script not parsing correctly

So I'm trying to pull the number of hours worked and the date worked from a table in my companies database to make a chart in Power BI through a streaming data set. I'm using powershell to parse a JSON file
Here's a JSON sample:
{"COUNT":"334","DISPLAY_LIST_START":"1","DISPLAY_LIST_STOP":"334","STOP":"334","RECORD":[{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/23/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/24/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/26/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/30/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/01/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["4",["05/02/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/03/2018"]]},{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/07/2018"]]},
I know it's not the best in terms of organization, but it's all I have to work with.
Here's the powershell code I have so far:
Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
$json.RECORD | Foreach-Object {
Write-Output "Checking Records"
$hours = 0
$date = ""
$json.FIELD | Foreach-Object{
Write-Output "Checking Field"
if ($_ -match '\d{1,2}/\d{1,2}\/d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" =$date
}
}
Invoke-RestMethod -Method Post -Uri "$endpoint" jlk-Body (ConvertTo-Json #($payload))
I need to parse through each record and pull the values of the hours (the numeric value in the JSON) and the Date (the date value).
When I run the code I don't get any errors, but it doesn't seem to be reaching the -match or the else statements. I tried logging the output on both and it returns nothing.
Is there something wrong with my loops?
I'm brand new to powershell and most of this code I got from help from other people, but I understand what its doing for the most part.
Also, anyone who knows about streaming datasets, will pulling this this way even give me what I want?
Store the Invoke-Webrequest values in your $json first. You missed that point; that's why you are getting Null.I don't know it is a typo or a miss.
Secondly, you $json.RECORD is wrong because it doesnt have any Record in the response. What you are looking for is basically the content. $json.content is going to give you the content of numbers.
$json=Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
Your endpoint and invoke-restmethod has nothing to do with your json parsing. First handle the response in the loop and see what is the outcome you are getting. I have structured it but I have not worked on the JSON sample data as if now:
$json=Invoke-WebRequest -Uri "http://wya.works/rta_develop/xmlServlet?&command=retrieve&sql=select%20%5B%24Hours%5D%2C%20%5B%24Date%20Worked%5D%20from%20%5B%21HOURS%5D%20&attributesOnly=Date%20Worked%2C%20Hours&contentType=JSON&referer=&time=1595443368507&key=696a6768"
$json.content | Foreach-Object {
Write-Output "Checking Records"
$hours = 0
$date = ""
$json.FIELD | Foreach-Object{
Write-Output "Checking Field"
if ($_ -match '\d{1,2}/\d{1,2}\/d{4}'){
$date = $_
}
else {
$hours = $_
}
}
$payload = #{
"Hours" = $hours
"Date Worked" =$date
}
}
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
Invoke-RestMethod -Method Post -Uri "$endpoint" jlk-Body (ConvertTo-Json #($payload))
The root of your problem is this:
$json.RECORD | Foreach-Object {
$json.FIELD | Foreach-Object{
...
}
}
Your outer foreach-object is looping over each item in the RECORD array, but the inner foreach-object is trying to loop over top-level FIELD properties that don't actually exist!
However, I think you'll hit more problems further along your code if you try to troubleshoot what you've already got, and there's an easier way to do what you're trying to do...
First, your sample json isn't quite valid - there's a trailing comma and some unclosed brackets so I'm going to reformat it with some line breaks so it's easier to read, and then fix it.
Note - you'll get the text from your call to Invoke-WebRequest, or as #Lee_Dailey suggested, Invoke-RestMethod.
# reformat the json, fix it, and assign it to a variable using a "Here-String"
# (see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7#here-strings)
$text = #"
{
"COUNT":"334",
"DISPLAY_LIST_START":"1",
"DISPLAY_LIST_STOP":"334",
"STOP":"334",
"RECORD":[
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/23/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/24/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/26/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["04/30/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/01/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["4",["05/02/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/03/2018"]]},
{"SESSION_ID":"c_a7FdTFicmxBJh9kln4V6gKxz_QErcufE7URF9m","FIELD":["6",["05/07/2018"]]}
]
}
"#
Then, we'll parse it - i.e. convert it from a string into an object model with well-defined properties:
# parse the json text
$json = $text | ConvertFrom-Json
And then process each record in turn:
$json.RECORD | ForEach-Object {
# get the number of hours worked. this is the first value in the FIELD array
# (note - the array starts at index 0 because it's zero-indexed)
$hours = $_.FIELD[0] # e.g. "6"
# get the "date worked". we need to get the second value (index 1) in
# the FIELD array, but this is a nested array, so once we've got the
# inner array we need to get the first value (index 0 again) from that
$date = $_.FIELD[1][0] # e.g. "04/23/2018"
# now we can build the payload...
$payload = #{
"Hours" = $hours
"Date Worked" = $date
}
# ...and invoke the api for this record
$endpoint = "https://api.powerbi.com/beta/d6cdaa23-930e-49c1-9d2a-0fbe648551b2/datasets/91466553-d719-420c-9e3e-73e748379263/rows?noSignUpCheck=1&key=SU5GRBBWuuEIDSjqHW5hdgJzSMCQ3qUQ9mGrBDanjgpExv6woY1Sa1c3PC1Wk3WHHn1N%2FEpIuVgzHHcw0JXwYw%3D%3D"
Invoke-RestMethod -Method Post -Uri "$endpoint" -Body (ConvertTo-Json #($payload))
}
Note the api call is inside the foreach loop, otherwise you end up calculating the $payload for each RECORD but only ever actually calling the api for the last one.
(I've also removed a spurious "jlk" from your final line, which is probably a typo).

How to parse/access JSON returned by Invoke-WebRequest in Powershell 4

I have the following call to an API in a powershell script (Powershell 4.0):
$Json = Invoke-WebRequest -Uri $RequestURL -UseBasicParsing -Headers $headers -ContentType 'application/json; charset=utf-8' -Method POST -Body $postParams -TimeoutSec 40
...and the content of the response (which is a string in JSON format) is written to a file:
Set-Content $path -Value $Json.Content
An example of a typical response...
{
"MyArray": [{
"MyField": "A1",
"MyField2": "A2"
}, {
"MyField": "B1",
"MyField2": "B2"
}]
}
All well and good, but now I have a requirement to parse the returned content as JSON and query some properties from within this Powershell script.
I presume I need to convert my string to 'proper' JSON and then to a powershell object in order to access the properties...so I have tried combinations of ConvertTo-Json and ConvertFrom-Json but can't ever seem to access it in anything other than a string. For example...
$x = $Json.Content | ConvertTo-Json
Write-Host $x.MyArray[0].MyField
$y = $x | ConvertFrom-Json
Write-Host $y[0].MyArray[0].MyField
In both cases above I get an error "Cannot index into a null array" suggesting that MyArray is null.
How do I convert my $Json response object into an object I can drill down into?
See ConvertFrom-Json
Converts a JSON-formatted string to a custom object or a hash table.
The ConvertFrom-Json cmdlet converts a JavaScript Object Notation
(JSON) formatted string to a custom PSCustomObject object that has a
property for each field in the JSON string.
Once you get the response converted to custom object or a hash table, you can access the individual properties
The link includes coding examples
This seems to work...
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
$x = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($Json.Content)
Write-Host $x.MyArray[0].MyField
...although not sure why yet.

Changing Slack CURL call into PowerShell

I've in the middle of writing a translation of a working Slack script from Bash to Powershell, and I'm stumbled on what is literally the meat of the problem; how to use Invoke-WebRequest to replace curl.
This is the curl command that works successfully in Bash on *nix systems:
curl \
-X POST \
-H "Content-type: application/json" \
--data "{
\"attachments\":
[
{
\"mrkdwn_in\": [\"text\"],
\"color\": \"$COLOUR\",
\"text\": \"$MESSAGE\",
}
]
}" \
https://hooks.slack.com/services/tokenpart1/tokenpart2
Note the $COLOUR and $MESSAGE variables are derived from elsewhere in the script (not the section I'm having trouble with).
I can't get it to work in PowerShell. My translation so far is:
$Body = #{
"attachments" = "[
{
"mrkdwn_in": ["text"],
"color": "$Colour",
"text": "$Message",
]"
}
$BodyJSON = $Body |convertto-JSON
Invoke-WebRequest -Headers -Method Post -Body "$BodyJSON" -Uri "https://hooks.slack.com/services/tokenpart1/tokenpart2" -ContentType application/json
This results in the following error:
At C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~~~~~~~~~~~~~~~~~~~~~
Unexpected token 'mrkdwn_in": ["text"],
"color": "$COLOUR",
"text": "$MESSAGE",
}
]"' in expression or statement.
At C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~
The hash literal was incomplete.At
C:\path-to-file-etc.ps1:53 char:11
+ "mrkdwn_in": ["text"],
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordEx
ception
+ FullyQualifiedErrorId : UnexpectedToken
Process exited with code 1
Process exited with code 1
Step Notify (PowerShell) failed
I have pretty much zero experience in Powershell. Also because this script has to be able to dropped onto a whole variety of boxes,I won't use any libraries or custom cmdlets or anything like that. Out of box methods or die.
Since you're using ConvertTo-Json anyway, it's simpler to construct the entire input as a (nested) custom object / hashtable, and let ConvertTo-Json handle all JSON formatting:
$Body = [pscustomobject] #{
attachments = , [pscustomobject] #{
mrkdwn_in = , 'text'
color = $Colour
text = $Message
}
}
$BodyJson = $Body | ConvertTo-Json -Depth 3
Note: You could substitute [ordered] for [pscustomobject] to use a hashtable with ordered keys instead; even omitting either would work, though seeing the entries in the resulting JSON in different order may be confusing.
Note the use of ,, the array-construction operator, to ensure that the attachments and mrkdwn_in entries are treated as arrays.
Additionally, since ConvertTo-Json only fully serializes to a default depth of 2 levels, -Depth 3 must be used to ensure that the value of entry mrkdwn_in is serialized as an array.
As for what you tried:
Your immediate problem (in addition to the missing } that Jeff Zeitlin points out in a comment on the question): You've neglected to escape the embedded " chars. in your multi-line string.
Therefore, as documentation topic Get-Help about_Quoting_Rules discusses, you can either use `" to embed double quotes inside "..." or use a here-string (#"<nl>....<n>"#).
Even with the syntax problems fixed, your code wouldn't work as intended, however, because ConvertTo-Json wouldn't preserve the pre-formatted JSON in the attachments entry and instead treat the string value as a string literal that needs escaping; here's a simple example:
#{
foo = "[
1,
2
]"
} | ConvertTo-Json
The above yields:
{
"foo": "[\n 1, \n 2 \n ]"
}