Making PowerShell “Posh”
Back in the days when DOS and Windows were separate programs, the default Command Prompt didn’t look exactly the way that it does now. I can’t recall if it w...
To borrow from a favorite web comic: whenever I learn a new skill, I concoct elaborate fantasy scenarios where it lets me save the day. Actually, it feels like I don’t need to concoct elaborate scenarios, the scenarios seem to find me after I’ve coincidentally learned an applicable skill.

This week, we lost track of the “good copy” of the script that parses the FHIR Operation Outcomes generated by one of our engines. The “integration record” component wasn’t quite in the right format. It’s definitely not the expected CSV, but it’s also not valid JSON. What to do?
In a previous blog, I discussed how we can parse JSON files, specifically the FHIR Operation Outcomes. The pipeline in question is the basis for a function that I’ve put in my $PROFILE.
function Get-OOIntegrationRecord {
param (
[Parameter(Mandatory, ValueFromRemainingArguments)]
[string]$outcome_file
)
Get-Content $outcome_file | ConvertFrom-Json | Select-Object -ExpandProperty extension `
| Where-Object url -eq 'http://upmc.com/fhir/integration/logging/StructureDefinition/integration-record' `
| Select-Object -ExpandProperty valueAttachment | Select-Object -ExpandProperty data | ConvertFrom-Base64
}
This function has already proven itself handy for pulling the original record out of an Operation Outcome, but I didn’t realize that I’d actually grab this tool for a real production issue. We have a script that pulls this same payload out of an operation outcome, parses it as a CSV row, adds it to a data structure, and then after it’s parsed a directory of files, it outputs the CSV to a file. One of our pipelines doesn’t output the right format. We have a separate copy of the script somewhere that can handle this format, but we’ve lost track of it. It might be where it belongs, but it wasn’t the currently-checked-out copy on my laptop and nobody else seemed to know if theirs was the good one except for the two who had unsuccessfully tried their copies.
I didn’t immediately know what was wrong with the formatting. I think I’d had the good copy of the script and kept it separate until recently when I reorganized my git directory. In this particular engine, when we create our error outcome, we write a Java PatientDocument object using .toString(). The resulting object looks almost like JSON, but not quite.
{ "key": "value", "key": "value" }
{key=value, key=value}
Fortunately, the key-value pairs can be easily parsed with ConvertFrom-StringData provided we get rid of the curly braces and put all of the key-value pairs on their own lines. The ease of doing this inline really blew me away…
$(Get-OOIntegrationRecord <json_file>) -replace '[{}]', '' -replace ', ', "`n" | ConvertFrom-StringData
All I had to do was wrap Get-OOIntegrationRecord with $() and then add two inline -replace, which can accept a RegEx… so a simple character class with the curly braces replaced with an empty string gets rid of those, and replacing the comma and space with a newline, noting that PowerShell uses the backtick ` to escape a newline to avoid confusing it with the backslash \ that Windows uses as the path separator.
Once we pipe that through ConvertFrom-StringData, the key-value pairs are turned into a Hashtable, which can easily convert to CSV with one last little transformation: another -replace to get rid of the null values.
$($(Get-OOIntegrationRecord <json_file>) -replace '[{}]', '' -replace ', ', "`n" | ConvertFrom-StringData | ConvertTo-Csv -Delimiter '|' -UseQuotes Never) -replace '\|null', '|'
Where this method really shines is when you pipe a bunch of these files through at once:
(Get-ChildItem *.json | ForEach-Object { $(Get-OOIntegrationRecord $_) -replace '[{}]', '' -replace ', ', "`n" | ConvertFrom-StringData } | ConvertTo-Csv -Delimiter '|' -UseQuotes Never) -replace '\|null', '|'
All of the objects roll up into a single CSV file. Not bad for data that wasn’t formatted correctly!
Back in the days when DOS and Windows were separate programs, the default Command Prompt didn’t look exactly the way that it does now. I can’t recall if it w...
Sometimes it’s the little things…
As a Data Engineer using Mirth Connect, it’s often tricky to review my team’s merge requests because the merge request document contains the XML representati...
When dealing with Python dependencies, it’s sometimes necessary to determine if a given project is using a given version of a library. Since our team is usin...
A while ago, I wrote about using a bare git repository to manage my dotfiles. That worked somewhat well, but it had some shortcomings. Since, I’ve started us...
One of my current projects involves some improvements to code that I wrote last year – specifically enhancing the “Patient Identity Fixer”, which is my pet p...
To borrow from a favorite web comic: whenever I learn a new skill, I concoct elaborate fantasy scenarios where it lets me save the day. Actually, it feels li...
This one bugged me for a few days. I don’t think this script is the best solution to the problem at hand, but it was still fun to figure out.
My team at work has a custom FHIR resource that we work with often called an Operation Outcome. We generate one of these for nearly every record we process, ...
a.k.a. Why I Don’t Use Nano
COVID-19 has us many of us working from home. My husband and I are both fortunate enough to be in this group. However, as a teacher, his schedule is very pre...
When I set up a Raspberry Pi, there are a few things that I have to do right out of the gate to make things easier on myself. They involve putting a few file...
While I don’t use Linux as my primary operating system, I do use it a lot both at home and at work. It can be frustrating to try to sync up configurations be...
Anyone who knows my husband or me knows that we really like our cold brew, and they also know that we make our own at home. Nitro cold brew has become a bit ...
So you want to run an OpenVPN Server on a Raspberry Pi? I did too, but it wasn’t easy to figure out the first time. If you want to skip all of this and do it...