In Powershell, when I use the invoke-webrequest cmdlet in my script, I get data returned in a JSON with a variety of data types. However, integers and dates do not seem to work correctly with the convertfrom-json cmdlet.
Here is an example of the code:
$json1 = Invoke-webrequest -URI $URI -Certificate $cert -Headers $header | convertfrom-json
A field located here should have numbers in it, and they do in the response before the pipeline to convertfrom-json:
Number = $json1.workers.workAssignments.Number
So, how can I extract the number and date fields? Preferably I would like to loop through and add every employee and every attribute to a datatable, disconnected recordset, pscustomobject, or psobject & then export to CSV.
I have coded each of the above objects several different times, and none of them have I gotten the integers or dates to export correctly. So, in lieu of posting a bunch of my solutions I figured I would keep it basic with the above and ask how to get the variable out of $json1 that contains integers and into some object to export to CSV.
I am not married to convertfrom-json whatsoever.
Example JSON:
{
"workers": [ {
"associateOID": "xxxxxxxx",
"workerID": {
"idValue": "xxxxxxxx"
},
"legalName": {
"givenName": "Lebron",
"middleName": "King",
"familyName1": "James",
"formattedName": "James, Lebron King"
},
"birthDate": "1985-01-01",
"baseRemuneration": {
"payPeriodRateAmount": {
"amountValue": 25,000,000,
"currencyCode": "USD"
},
"effectiveDate": "2016-01-01"}
When using convertfrom-json, that amount in "Amount Value" results in Zero no matter what method I use.
Related
I have two JSON files and want to transfer collection of objects from one file to another. Suppose, the from.json file contains property which represents collection of clients:
"Clients":
[
{
"Name": "Name1",
"Age": "12"
},
{
"Name": "Name2",
"Age": "14"
}
]
to.json file contains an empty collection, "Objects: []" ,which must be filled with objects from from.json. Each objects in toJson variable must contain additional property - Id, so eventually, my "to.json" file should look like this:
"Objects":
[
{
"Id": "{new-id}",
"Name": "Name1",
"Age": "12"
},
{
"Id": "{new-id}",
"Name": "Name1",
"Age": "12"
}
]
I've converted two files into variables:
$fromJson = (Get-Content -Raw -Path {fromPath}) | ConvertFrom-Json
$toJson = (Get-Content -Raw -Path {toPath}) | ConvertFrom-Json
I know that objects from fromJson to toJson can be transferred in the following manner:
toJson.Objects += fromJson.Clients, but that's not enough in my case. I think that it could be done by iterating through fromJson.Clients array but have no idea how to create an object and add it into toJson.Objects collection.
Here's a more efficient solution, based on:
Use of a calculated property with Select-Object, which allows you to place the new property first in the output objects.
Instead of building the array one by one with += (which is inefficient, because a new array must technically be created behind the scenes in every iteration), the solution below lets PowerShell collect the output objects of the Select-Object call in an array automatically (the [array] type constraint is needed to ensure that an array is created even if only one object happens to be output.)
# Sample input.
$fromJson = ConvertFrom-Json '{"Clients":[{"Name":"Name1","Age":"12"},{"Name":"Name2","Age":"14"}]}'
$toJson = ConvertFrom-Json '{ "Objects": [] }'
[array] $toJson.Objects =
$fromJson.Clients |
Select-Object #{ Name='Id'; Expression = { [string] (New-Guid) } }, *
$toJson | ConvertTo-Json -Depth 3 # append | Set-Content as needed.
Kind of new to the PowerShell, but after a bit of investigation came up with the following solution:
fromJson.Clients | ForEach-Object {
$_ | Add-Member -MemberType NoteProperty -Name 'Id' -Value ([guid]::NewGuid().Guid.ToString())
$toJson += $_
}
...
$toJson | ConvertTo-Json | Out-File {to.json_path}
Frankly, don't know if that is a 'proper' way to do that, but generally it works for that particular case. For now, see no other solution.
I am currently trying to prepare a JSON body for an API call, which should look something like this
curl -XPOST -H 'Authorization: Bearer ***API*KEY***' -H 'Content-Type: application/json' http://127.0.0.1:9000/api/alert -d '{
"title": "Other alert",
"description": "alert description",
"type": "external",
"source": "instance1",
"sourceRef": "alert-ref",
"severity": 3,
"tlp": 3,
"artifacts": [
{ "dataType": "ip", "data": "127.0.0.1", "message": "localhost" },
{ "dataType": "domain", "data": "thehive-project.org", "tags": ["home", "TheHive"] },
],
"caseTemplate": "external-alert"
}'
The problem however is that my json body which I create with powershell has weird characters and don't see where the problem is. This is my JSON Body
{
"tlp": 1,
"source": "Test",
"title": "Test Alert1",
"artifacts": "{\r\n \"dataType\": \"ip\",\r\n \"data\": \"127.0.0.1\"\r\n}",
"type": "external",
"sourceRef": "1",
"description": "Test",
"severity": 1
}
I create this JSON body as follows. I have already tried CustomObjects and Hashtables, but always get the same output
$body = #{
title = "$title"
description = "$Alert_Description"
type ="external"
source ="$Source"
sourceRef ="$SourceRef"
severity = $Severity
tlp = $tlp
$artifacts = [PSCustomObject]#{
dataType=ip
data=127.0.0.1}
}| ConvertTo-Json
$JsonBody = $body | ConvertTo-Json
Can somebody give me a hint? I have no clue anymore
First, fix your hashtable so that it creates the intended JSON representation, and only apply ConvertTo-Json once:
$body = [ordered] #{
title = $title
description = $Alert_Description
type = 'external'
source = $Source
sourceRef = $SourceRef
severity = $Severity
tlp = $tlp
artifacts = , [pscustomobject] #{
dataType = 'ip'
data = '127.0.0.1'
}
} | ConvertTo-Json
Variables don't need enclosing in "..." unless you explicitly want to convert their values to strings.
Literal textual information such as ip does require quoting ('...', i.e. a verbatim string literal, is best).
The artifacts property is an array in your sample JSON, so the unary form of ,, the array constructor operator is used to wrap the [pscustomobject] instance in a single-element array.
If you have multiple objects, place , between them; if the array was constructed previously and stored in a variable named, say, $arr, use artifacts = $arr.
Use of [ordered], i.e. the creation of an ordered hashtable isn't strictly necessary, but makes it easier to compare the resulting JSON representation to the input hashtable.
Generally keep in mind that explicit use of a -Depth argument with ConvertTo-Json is situationally required in order to ensure that properties aren't truncated - see this post for more information. However, with the hashtable in your question this happens not to be a problem.
Second, since you're sending the json to an external program, curl (note that in Windows PowerShell you'd have to use curl.exe in order to bypass the built-in curl alias for Invoke-WebRequest, an additional escaping step is - unfortunately - required up to at least v7.1 - though that may change in v7.2: The " instances embedded in the value of $body must manually be escaped as \", due to a long-standing bug in PowerShell:
curl -XPOST -H 'Authorization: Bearer ***API*KEY***' -H 'Content-Type: application/json' `
http://127.0.0.1:9000/api/alert -d ($body -replace '([\\]*)"', '$1$1\"')
Note: With your particular sample JSON, -replace '"', '\"' would do.
See this answer for more information.
Slap a "-Depth 50" on your ConvertTo-Json, that should solve your issue.
Like here: ConvertTo-Json flattens arrays over 3 levels deep
Currently, I'm attempting to call upon an API to run a POST, with JSON data as the body. So I was wondering if anyone would be able to tell me how I need to format the text below inside the variable $postParams. I'm pretty new at working with JSON so I'm having so trouble with this.
Currently, I only have the following and don't know what to do about the second line on.
$postParams = #{name='Example'}
Here's is the entire data I was hoping to add to $postParams. So if you could help me with the 2nd, 4th, and 8th that'd be awesome. Thanks!
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
You'll need a here-string and ConvertFrom-Json.
here-string:
Quotation marks are also used to create a here-string. A here-string is a single-quoted or double-quoted string in which quotation marks are interpreted literally. A here-string can span multiple lines. All the lines in a here-string are interpreted as strings, even though they are not enclosed in quotation marks.
The resulting code:
# Use a PowerShell here string to take JSON as it is
$jsonString = #"
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
"#
# Pipe the string to create a new JSON object
$jsonObject = $jsonString | ConvertFrom-Json
# The resulting JSON object has properties matching the properties in the orig. JSON
$jsonObject.name
$jsonObject.url
# Nested property
$jsonObject.template.name
# Nested property in array
$jsonObject.groups[0].name
I've posted an online version of the above code at tio.run, so you can play around with it.
If you want to update several properties of the $jsonObject you can do the following:
$jsonObject.name = "NEW NAME"
$jsonObject.url = "NEW URL"
$jsonObject | ConvertTo-Json
ConvertTo-Json will take your object and create an appropriate JSON string:
{
"name": "NEW NAME",
"template": {
"name": "Template"
},
"url": "NEW URL",
"page": {
"name": "Landing Page"
},
"smtp": {
"name": "Sending Profile"
},
"launch_date": "2019-10-08T17:20:00+00:00",
"send_by_date": null,
"groups": [
{
"name": "test group"
}
]
}
If you $jsonObject has more than two levels of depth, use the -Depth parameter, otherwise not all object information will be included in the JSON string.
ConvertTo-Json:
-Depth
Specifies how many levels of contained objects are included in the JSON representation. The default value is 2.
Here is a tio.run link to a ConvertTo-Json example.
Hope that helps.
I can't test it currently, but try this.
$postParams = #'
{
"name":"Example",
"template":{"name":"Template"},
"url":"http://localhost",
"page":{"name":"Landing Page"},
"smtp":{"name":"Sending Profile"},
"launch_date":"2019-10-08T17:20:00+00:00",
"send_by_date":null,
"groups":[{"name":"test group"}]
}
'#
Make a hashtable, then convert to JSON:
$Hashtable = #{
Key1 = "Value1"
Key2 = "Value2"
}
$Json = $Hashtable | ConvertTo-Json
I am reading data from a json file via powershell, with the ultimate goal of updating said file. I need to split the data in a chunk I want to keep and a chunk I want to update, and to complicate the matter further, the place where I need to split the text varies throughout a foreach-loop, thus I need that part to come from a variable.
I have tried .split/-split and .replace/-replace in numerous configurations, but it seems this is harder than one would assume in powershell.
All the below files in the same folder.
Json (json.json):
{
"Section1": {
"Heading1": [
"Thing1",
"Thing2"
]
},
"Section2": [
"Thing1",
"Thing2"
]
}
Powershell (powershell.ps1):
$originalJsonString = Get-Content -Path ".\json.json"
$SplitTarget = "Section2"
$JsonString = {This is the part that I am iffy about}
Write-Output $JsonString
The output I want from the above would be
{
"Section1": {
"Heading1": [
"Thing1",
"Thing2"
]
},
I have tried just about everything I can think of in relation to split and replace, but yet the solution alludes me.
Note in the solution above, that it is an important factor that $originalJsonString is split (or whatever) by $SplitTarget, and not "Section2", as that is also a factor in my equation.
This is my first time asking, so if I am doing something wrong I apologise.
Thank you.
Edit:
It is only fair that I add the reason I don't convert to an object.
The syntax which powershell exports when converting json to an object and back is undesirable for my use.
However, if using an object is the ONLY way, and slitting is out of the question, then another solution must be found.
Thank you.
Edit:
If objects was the way to go, I ended up finding a way more complicated way to format the .json the way I wanted it.
Thanks everyone.
What you should do is read the json file in using :
$json = Get-Content .\json.json -raw | ConvertFrom-Json
I faked that here using a 'here-string':
$json = #"
{
"Section1": {
"Heading1": [
"Thing1",
"Thing2"
]
},
"Section2": [
"Thing1",
"Thing2"
]
}
"# | ConvertFrom-Json
Next, define what you want to keep or update:
$sectionToKeep = "Section1"
$sectionToUpdate = "Section2"
To see what is in there use $json.$sectionToKeep | ConvertTo-Json
{
"Heading1": [
"Thing1",
"Thing2"
]
}
Next, update $section2 leaving everything else untouched. I am writing an object that stores an array, just like in $sectionToKeep:
$json.$sectionToUpdate = #{'Heading2' = 'Thing3', 'Thing4'}
and finally output (or write back to file) the new complete json:
$json | ConvertTo-Json
Using your example gives you this:
{
"Section1": {
"Heading1": [
"Thing1",
"Thing2"
]
},
"Section2": {
"Heading2": [
"Thing3",
"Thing4"
]
}
}
Hope that helps
Not sure to really understand your question, but I would convert the Json to an object and then filter the sub data and create a file again
$obj = Get-Content json.json -raw | ConvertFrom-Json
If you are looking to just isolate a section and create a new json file, then you can use the following:
$file = Get-Content .\json.json | ConvertFrom-Json
$file | Select-Object Section1 | ConvertTo-Json | Set-Content New.json
Apologies for my lack of powershell knowledge, have been searching far and wide for a solution as i am not much of a programmer.
Background:
I am currently trying to standardise some site settings in Incapsula. To do this i want to maintain a local XML with rules and use some powershell to pull down the existing rules and compare them with what is there to ensure im not doubling up. I am taking this approach of trying to only apply the deltas as:
For most settings incapsula is not smart enough to know it already exists
What can be posted to the API is different varies from what is returned by the API
Examples:
Below is an example of what the API will return on request, this is in a JSON format.
JSON FROM WEBSITE
{
"security": {
"waf": {
"rules": [{
"id": "api.threats.sql_injection",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/thisurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 256354634
}]
}, {
"id": "api.threats.cross_site_scripting",
"action": "api.threats.action.block_request",
"exceptions": [{
"values": [{
"urls": [{
"value": "google.com/anotherurl",
"pattern": "EQUALS"
}],
"id": "api.rule_exception_type.url",
"name": "URL"
}],
"id": 78908790780
}]
}]
}
}
}
And this is the format of the XML with our specific site settings in it
OUR XML RULES
<waf>
<ruleset>
<rule>
<id>api.threats.sql_injection</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/thisanotherurl</url>
</exception>
</exceptions>
</rule>
<rule>
<id>api.threats.cross_site_scripting</id>
<exceptions>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl</url>
</exception>
<exception>
<type>api.rule_exception_type.url</type>
<url>google.com/anotherurl2</url>
</exception>
</exceptions>
</rule>
</ruleset>
</waf>
I have successfully been able to compare other settings from the site against the XML using the compare-object command, however they had a bit simpler nesting and didn't give me as much trouble. I'm stuck to whether it is a logic problem or a limitation with compare object. An example code is below, it will require the supplied json and xml saved as stack.json/xml in the same directory and should produce the mentioned result :
$existingWaf = Get-Content -Path stack.json | ConvertFrom-Json
[xml]$xmlFile = Get-Content -Path stack.xml
foreach ($rule in $xmlFile)
{
$ruleSet = $rule.waf.ruleset
}
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
foreach ($xmlRule in $ruleSet)
{
if ($xmlRule.rule.id -eq $siteRule.id)
{
write-output "yes"
$delta = Compare-Object -ReferenceObject #($siteRule.exceptions.values.urls.value | Select-Object) -DifferenceObject #($xmlRule.rule.exceptions.exception.url | Select-Object) -IncludeEqual | where {$xmlRule.rule.id -eq $siteRule.id}
$delta
}
}
}
This is kind of working but not quite what i wanted. I do get a compare between the objects but not for the specific id's, it shows me the results below:
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl =>
google.com/anotherurl2 =>
google.com/anotherurl ==
google.com/thisurl =>
google.com/thisanotherurl =>
google.com/anotherurl2 =>
Where as i am more after
InputObject SideIndicator
----------- -------------
google.com/thisurl ==
google.com/thisanotherurl =>
google.com/anotherurl ==
google.com/anotherurl2 =>
Hopefully that makes sense.
Is it possible to only do the compares only on the values where the ids match?
Please let me know if you have any further questions.
Thanks.
The problem was your iteration logic, which mistakenly processed multiple rules from the XML document in a single iteration:
foreach ($xmlRule in $ruleSet) didn't enumerate anything - instead it processed the single <ruleset> element; to enumerate the child <rule> elements, you must use $ruleSet.rule.
$xmlRule.rule.exceptions.exception.url then implicitly iterated over all <rule> children and therefore reported the URLs across all of them, which explains the extra lines in your Compare-Object output.
Here's a streamlined, annotated version of your code:
$existingWaf = Get-Content -LiteralPath stack.json | ConvertFrom-Json
$xmlFile = [xml] (Get-Content -raw -LiteralPath stack.xml )
# No need for a loop; $xmlFile is a single [System.Xml.XmlDocument] instance.
$ruleSet = $xmlFile.waf.ruleset
foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
# !! Note the addition of `.rule`, which ensures that the rules
# !! are enumerated *one by one*.
foreach ($xmlRule in $ruleSet.rule)
{
if ($xmlRule.id -eq $siteRule.id)
{
# !! Note: `$xmlRule` is now a single, rule, therefore:
# `$xmlRule.rule.[...]-> `$xmlRule.[...]`
# Also note that neither #(...) nor Select-Object are needed, and
# the `| where ...` (Where-Object) is not needed.
Compare-Object -ReferenceObject $siteRule.exceptions.values.urls.value `
-DifferenceObject $xmlRule.exceptions.exception.url -IncludeEqual
}
}
}
Additional observations regarding your code:
There is no need to ensure that operands passed to Compare-Object are arrays, so there's no need to wrap them in array sub-expression operator #(...). Compare-Object handles scalar operands fine.
... | Select-Object is a virtual no-op - the input object is passed through[1]
... | Where-Object {$xmlRule.rule.id -eq $siteRule.id} is pointless, because it duplicates the enclosing foreach loop's condition.
Generally speaking, because you're not referencing the pipeline input object at hand via automatic variable $_, your Where-Object filter is static and will either match all input objects (as in your case) or none.
[1] There is a subtle, invisible side effect that typically won't make a difference: Select-Object adds an invisible [psobject] wrapper around the input object, which on rare occasions does cause different behavior later -
see this GitHub issue.