I have a JSON file, called data.json with contents:
{
"environment": "${environment}",
"url": "https://${environment}.example.com"
}
I have a PowerShell script, where I am attempting to read in the JSON file and use string interpolation to substitute the value of the PowerShell $environment variable into the JSON file.
script.ps1:
$environment = "qa"
$json = Get-Content -Path ".\data.json"
$formatted = Invoke-Expression "`"${json}`""
Write-host $formatted
When I run this, I can't seem to get around problems with the value being sent to Invoke-Expression as being null or invalid character ':' from the JSON.
Is there an easier/better way of trying to read in a JSON file and perform string interpolation on it?
I am trying to avoid using ExpandString(), string concatenation, string.Replace() and -f.
The end goal, I'm hoping to have a Json PowerShell object (using ConvertFrom-Json) to be:
{
"environment": "qa",
"url": "https://qa.example.com"
}
Thanks in advance!
Try like this:
$environment = 'qa'
$json = Get-Content -Path '.\data.json' -Raw # be sure to add -Raw switch
$formatted = Invoke-Expression "#""`n$json`n""#"
Write-Host $formatted
Thank you #Bill_Stewart and #Mathias R. Jessen - I explored a bit more and it seems like $ExecutionContext.InvokeCommand.ExpandString() works. To be honest, I'm not sure what I read yesterday that led me to believe that ExpandString() would not work. Thank you for your help, much appreciated.
Related
Taking an example from JQs official site, I am trying to parse the jsonArray in below example screenshot.
I am trying to get both jsonObjects, so that I can do something with them for my problem statement. However, when I try to do this in PowerShell using below code:
$inputJson = '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]'
$items = $inputJson | jq '.[]'
I get not two $items objects but 8 $items psobjects. As seen in the second screenshot.
Any pointers, on what changes I can make to get only two jsonObjects here?
The question is confusing. All jq does is output strings. Powershell will make each line a new string array element. The only way I see it doing what you want, is with jq's --compact-output option, which outputs one object (to jq) per line. That seems to be the way that online example was run, anyway. But to powershell, these are two strings. They would be objects to powershell only after using convertfrom-json.
$items = $inputJson | jq '.[]' --compact-output
$items
{"name":"JSON","good":true}
{"name":"XML","good":false}
Although still dont know how to do it with jq, here is another way of achieving what I wanted using Powershell.
$inputJson = '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' | ConvertFrom-Json
write-host "`nNr of jsonObjects:"
write-host $inputJson.Length
write-host "`nfirst jsonObject:"
Write-Host $inputJson[0]
write-host "`njsonObjects value:"
$value = $inputJson[0] | ConvertTo-Json
Write-Host $value
Result is:
Nr of jsonObjects:
2
first jsonObject:
#{name=JSON; good=True}
jsonObjects value:
{
"name": "JSON",
"good": true
}
How does one convert a text file's contents into a string and then insert this string into a JSON file?
For example, if a file contains:
this
is
a
sample
file
The script would generate:
"this\r\nis\r\na\r\nsample\r\nfile"
To insert into a JSON template:
"something":"<insertPoint>"
To produce:
"something":"this\r\nis\r\na\r\nsample\r\nfile"
I'm using Powershell 5 and have managed to load the file, generate some JSON and insert it by running:
# get contents and convert to JSON
$contentToInsert = Get-Content $sourceFilePath -raw | ConvertTo-Json
# write in output file
(Get-Content $outputFile -Raw).replace('<insertPoint>', $contentToInsert) | Set-Content $outputFile
However, a lot of other, unwanted fields are also added.
"something":"{
"value": "this\r\nis\r\na\r\nsample\r\nfile"
"PSPath": "C:\\src\\intro.md",
"PSParentPath": "C:\\src",
"PSChildName": "intro.md",
etc...
Ultimately, I'm trying to send small rich text segments to a web page via JSON but want to edit and store them locally using Markdown. If this doesn't make sense and there's a better way of sending these then please let me know also.
iRon's answer helpfully suggests not using string manipulation to create JSON in PowerShell, but to use hashtables (or custom objects) to construct the data and then convert it to JSON.
However, that alone does not solve your problem:
PS> #{ something = Get-Content -Raw $sourceFilePath } | ConvertTo-Json
{
"something": {
"value": "this\nis\na\nsample\nfile\n",
"PSPath": "/Users/mklement/Desktop/pg/lines.txt",
# ... !! unwanted properties are still there
}
The root cause of the problem is that Get-Content decorates the strings it outputs with metadata in the form of NoteProperty properties, and ConvertTo-Json currently invariably includes these.
A proposal to allow opting out of this decoration when calling Get-Content can be found in GitHub issue #7537.
Complementarily, GitHub issue #5797 suggests that ConvertTo-Json should ignore the extra properties for primitive .NET types such as strings.
The simplest workaround is to access the underlying .NET instance with .psobject.baseobject, which bypasses the invisible wrapper object PowerShell uses to supply the extra properties:
PS> #{ something = (Get-Content -Raw $sourceFilePath).psobject.baseobject } |
ConvertTo-Json
{
"something": "this\nis\na\nsample\nfile\n"
}
Just a general recommendation apart from the actually issue described by #mklement0 and metadata added to the Get-Content results:
Do not poke (replace, insert, etc.) in any Json content.
Instead, modify the object (if necessary, use ConvertFrom-Json to restore the object) prior converting it into (ConvertTo-Json) a Json file.
In this example, I would use a hash-table with a here-string for this:
#{'something' = #'
this
is
a
sample
file
'#
} | ConvertTo-Json
Result:
{
"something": "this\nis\na\nsample\nfile"
}
You can use the Out-String cmdlet to coerce the output of Get-Content into a flat string first:
#{ "something" = (Get-Content lines.txt | Out-String) } | ConvertTo-Json
This produces:
{
"something": "this\r\nis\r\na\r\nsample\r\nfile\r\n"
}
I have a JSON file which looks like this:
{
"body": {
"mode": "raw",
"raw": "{\n \"id\": \"middleware://so/998655555{{sguid}}\",\n \"reference\": \"998655555{{sguid}}\",\n \"branchCode\": \"1234\"\n }"
}
}
Now, I want to output the value of e.g. "branchCode".
I tried it with the following PowerShell commands:
$path = "file.json"
$raw = Get-Content $path -raw
$obj = ConvertFrom-Json $raw
$obj.body.raw.branchCode
Unfortunately, I don't get the value. It seems like PowerShell has a problem to retrieve the value from the raw json.
I would be very happy if someone could help me.
The value of "raw" is itself JSON.
You have to convert from JSON twice.
$data = Get-Content "file.json" -Raw -Encoding UTF8 | ConvertFrom-Json
$body = $data.body.raw | ConvertFrom-Json
$body.branchCode
prints
1234
Note that you should always explicitly specify an encoding when reading from and writing to text files. For the purposes of JSON (and, frankly, for pretty much every other purpose), UTF-8 is the appropriate choice.
Unfortunately Get-Content/Set-Content do not default to UTF-8. Save yourself from trouble by always specifying an encoding when working with text files.
I would like to read JSON file to get the list of application names and their respective uninstall strings and pass them as arguments in power shell to uninstall the apps irrespective of whether the apps are Windows Installer(MSI) or setup. Can someone please suggest me how to do this. Thanks
Use the ConvertFrom-JSON.
This will create a PSObject out of the JSON
$TEST2 = $TEST | ConvertFrom-Json
Thank you for suggesting the answer, that worked. I used the following cmd-line
$J = Get-Content -Raw -Path $scriptDirectory\Files\XXX.json | ConvertFrom-Json
and passed the AppName property as given below to fetch the application names:
$AppName = $J |fl -Property AppName.
FYI.. my JSON file content looked as follows:
[
{
"AppName": "xxxxx",
"AppVersion": "aa.bb"
},
{
"AppName": "yyy",
"AppVersion": "aa.bb.cc"
}
]
I am trying to write a powershell script that reads a file and prints "true" if it is a valid JSON file. I am using Powershell v3.0 and this is what I have right now :
$text = Get-Content .\filename.txt -Raw
$powershellRepresentation = $text | ConvertFrom-Json
How do I check the return code? I mean I want something like this :
if(file not a JSON file){
Write-Host "not JSON"
}
else{
Write-Host "True"
}
UPDATE 2021: PowerShell 6 and newer versions
PowerShell 6 brings a brand new Test-Json cmdlet. Here is the reference.
You can simply pass the raw file content directly to the Test-Json cmdlet.
$text = Get-Content .\filename.txt -Raw
if ($text | Test-Json) {
$powershellRepresentation = ConvertFrom-Json $text -ErrorAction Stop;
Write-Host "Provided text has been correctly parsed to JSON";
} else {
Write-Host "Provided text is not a valid JSON string";
}
PowerShell 5 and earlier versions
There is no Test-Json cmdlet in these versions, so the best way is to put your ConvertFrom-Json cmdlet inside a try ... catch block
try {
$powershellRepresentation = ConvertFrom-Json $text -ErrorAction Stop;
$validJson = $true;
} catch {
$validJson = $false;
}
if ($validJson) {
Write-Host "Provided text has been correctly parsed to JSON";
} else {
Write-Host "Provided text is not a valid JSON string";
}
If you encounter this question and can use PowerShell 6 or later, there is now a Test-Json cmdlet. It can also not just validate that it's valid JSON, but that it conforms to a particular JSON schema using the -Schema param.
Example
$isValid = Get-Content .\filename.txt -Raw | Test-Json
if($isValid){
Write-Host "not JSON"
}
else{
Write-Host "True"
}
ARM Template Warning
A note for users looking to validate an ARM template via -Schema (I can't imagine a more perfect use case). At the time of writing, there are one or more bugs in the underlying library Test-Json uses for validation, NJsonSchema, and it is not possible to validate an ARM template.
GitHub Issues
PowerShell Issue #9560
NJsonSchema Issue #588
I don't think that it exists an other solution than catching the exception using ConvertFrom-Json.
ConvertFrom-JSON would work but only for a JSON object < 2MB in size.
For higher you can use JavaScriptSerializer class
try
{
$jsser = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$jsser.MaxJsonLength = $jsser.MaxJsonLength * 10
$jsser.RecursionLimit = 99
$outObject = $jsser.DeserializeObject($json)
}
catch
{
Write-Host "Error converting $text to JSON"
}
ConvertFrom-Json shd be the appropriate way to go.
Test-Json unfortunately has alot of known unsupported JSON Types.
I.E. it cannot parse Json-Arrays or Primitives properly leading to falsely assuming it has wrong JSON-Syntax.