Home > Mobile >  How can I convert JSON-like string to PowerShell object?
How can I convert JSON-like string to PowerShell object?

Time:06-10

A Microsoft utility returns strings in the following format:

"Author: First.Last; Name: RootConfiguration; Version: 2.0.0; GenerationDate: 06/01/2022 13:18:10; GenerationHost: Server;"

I would like to convert those strings into simple objects. If this were true JSON, I'd just use ConvertFrom-JSON. To reinvent the wheel as little as possible, what's the most straightforward way to convert that into an object (with keys Author, Name, Version, GenerationDate, GenerationHost, with the obvious values. It's fine if the values are all treated as dumb strings.

If "you just have to grind it out by tokenizing the string bite by bite" is the answer, I can do that, but it seems there should be a simpler way, like if I could tell ConvertFrom-JSON (or even ConvertFrom-String!) "Do your thing, but process the semicolons as newlines, accept spaces on the right hand side, etc."

CodePudding user response:

  • A solution that combines manual parsing with ConvertFrom-StringData, but note that input order of the entries isn't preserved, given that the latter returns a [hashtable] instance with inherently unordered entries:
# Sampe input string.
$str = 'Author: First.Last; Name: RootConfiguration; Version: 2.0.0; GenerationDate: 06/01/2022 13:18:10; GenerationHost: Server;'

# Replace ":" with "=", split into individual lines, so
# that ConvertFrom-StringData recognizes the format.
$str -replace ': ', '=' -replace '; ?', "`n" | ConvertFrom-StringData

# Note: The above outputs a [hashtable].
#       You could cast it to [pscustomobject], as shown below,
#       but the input order of entries is lost either way.

As zett42 points out, if the values (as opposed to the keys) in the input string contained \ chars., they'd need to be doubled in order to be retained as such - see his comment below.

  • A solution with manual parsing only:
# Sampe input string.
$str = 'Author: First.Last; Name: RootConfiguration; Version: 2.0.0; GenerationDate: 06/01/2022 13:18:10; GenerationHost: Server;'

# Initialize an ordered hashtable (dictionary)
$dict = [ordered] @{}

# Split the string by ";", then each entry into key and value by ":".
$str -split '; ?' | 
  ForEach-Object { $key, $value = $_ -split ': ', 2; $dict[$key] = $value }

# Convert the ordered hashtable (dictionary) to a custom object.
[pscustomobject] $dict

CodePudding user response:

I usually don't answer questions that don't have a coding attempt but, figured this might help others. Given that the delimiter is a semicolon, I was thinking of converting to CSV first but, would have to worry about the header next. So, instead of converting to CSV, we can use the delimiter to split the results at that and process the values one at a time:

"Author: First.Last; Name: RootConfiguration; Version: 2.0.0; GenerationDate: 06/01/2022 13:18:10; GenerationHost: Server;".Split(";").Trim() | 
    ForEach-Object -Process {
        $header,$value = $_ -split ":",2
        New-Object -TypeName PSCustomObject @{
            $header = $value
        }
    } | ConvertTo-Json

To make this work we need to split at just the first colon (:) leaving the rest intact; incase there's others in the value like you see in the GenerationDate property.

  • This was achieved using $_ -split ":",2.

Finally, the rest was just left to assign the header to and value to a PSCustomObject and conver the results to JSON using ConvertTo-Json.


Note: I am restricted to a "strict language mode" on my work system so it's best to use the type-accelarator of [PSCustomObject]@{..} to create the object, rather than New-Object.

CodePudding user response:

Complementing the existing helpful answers, here is another one using the Regex.Matches() function:

$testInput = 'Author: First.Last; Name: RootConfiguration; Version: 2.0.0; GenerationDate: 06/01/2022 13:18:10; GenerationHost: Server;'

# Create a temporary, ordered Hashtable to collect keys and values in the original order.
$ht = [ordered] @{}  

# Use a regular expression to find all key/value pairs.
foreach( $match in [regex]::Matches( $testInput, '\s*([^:] ):\s*([^;] );') ) {
    # Enter a key/value pair into the Hashtable
    $ht[ $match.Groups[1] ] = $match.Groups[2] 
}

# Convert the temporary Hashtable to PSCustomObject.
[PSCustomObject] $ht

Output:

Author         : First.Last
Name           : RootConfiguration
Version        : 2.0.0
GenerationDate : 06/01/2022 13:18:10
GenerationHost : Server
  • The RegEx pattern consists of two capturing groups ( ), where the first one captures a key and the second one captures a value.
  • For a detailed explanation see regex101, where you can also play around with the pattern.
  • Related