I have a String
in the following format and need help converting it to a data structure that can easily access the key value pairs inside.
@{7068="@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}";7602="@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}";...}
I have tried using ConvertFrom-String
however I was unable to manipulate the String
properly to meet this format.
Given this input...
$testRecords = [Ordered] @{
0 = "@{}"; # No values
1 = "@{DekId=1}"; # Single value
2 = "@{DekId=1+1=2}" # Single value with equal sign
10 = "@{ }"; # No values (with padding)
11 = "@{ DekId=1 }"; # Single value (with padding)
12 = "@{ DekId=1+1=2 }" # Single value with equal sign (with padding)
# +------------------+--------------------+----------------+
# | Separating space | Trailing semicolon | Trailing space |
# +------------------+--------------------+----------------+
100 = "@{First=A B C;Second=X Y Z}"; # | No | No | No |
101 = "@{First=A B C;Second=X Y Z }"; # | No | No | Yes |
102 = "@{First=A B C;Second=X Y Z;}"; # | No | Yes | No |
103 = "@{First=A B C;Second=X Y Z; }"; # | No | Yes | Yes |
104 = "@{First=A B C; Second=X Y Z}"; # | Yes | No | No |
105 = "@{First=A B C; Second=X Y Z }"; # | Yes | No | Yes |
106 = "@{First=A B C; Second=X Y Z;}"; # | Yes | Yes | No |
107 = "@{First=A B C; Second=X Y Z; }"; # | Yes | Yes | Yes |
# First property empty # +------------------+--------------------+----------------+
200 = "@{First=;Second=X Y Z}"; # | No | No | No |
201 = "@{First=;Second=X Y Z }"; # | No | No | Yes |
202 = "@{First=;Second=X Y Z;}"; # | No | Yes | No |
203 = "@{First=;Second=X Y Z; }"; # | No | Yes | Yes |
204 = "@{First=; Second=X Y Z}"; # | Yes | No | No |
205 = "@{First=; Second=X Y Z }"; # | Yes | No | Yes |
206 = "@{First=; Second=X Y Z;}"; # | Yes | Yes | No |
207 = "@{First=; Second=X Y Z; }"; # | Yes | Yes | Yes |
# Second property empty # +------------------+--------------------+----------------+
300 = "@{First=A B C;Second=}"; # | No | No | No |
301 = "@{First=A B C;Second= }"; # | No | No | Yes |
302 = "@{First=A B C;Second=;}"; # | No | Yes | No |
303 = "@{First=A B C;Second=; }"; # | No | Yes | Yes |
304 = "@{First=A B C; Second=}"; # | Yes | No | No |
305 = "@{First=A B C; Second= }"; # | Yes | No | Yes |
306 = "@{First=A B C; Second=;}"; # | Yes | Yes | No |
307 = "@{First=A B C; Second=; }"; # | Yes | Yes | Yes |
# +------------------+--------------------+----------------+
7068 = "@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}";
7602 = "@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}";
}
...the following uses regular expressions to extract the surrounding @{ }
and then string splitting to parse what's inside into [Ordered]
hashtable instances...
foreach ($pair in $testRecords.GetEnumerator())
{
Write-Host '=================================================='
if ($pair.Value -notmatch '@{\s*(?<Body>.*)\s*}')
{
Write-Warning "Pattern failed to match input ""$($pair.Value)""."
}
else
{
$properties = [Ordered] @{}
$bodyText = $Matches['Body']
if (-not [String]::IsNullOrWhiteSpace($bodyText))
{
foreach ($propertyText in $bodyText -split ';\s*')
{
# In case the property value contains an equal sign, split
# on only the first =, producing a two-element array
$propertyName, $propertyValue = $propertyText -split '=', 2
if (-not [String]::IsNullOrEmpty($propertyName))
{
$properties[$propertyName] = $propertyValue
}
}
}
Write-Host "Parsed input ""$($pair.Value)"" to $($properties.GetType().Name) with Count = $($properties.Count)"
$properties.GetEnumerator() `
| Select-Object -Property `
'Name', `
'Value', `
@{
Name = 'PrintableValue';
Expression = {
return $(
if ($_.Value -eq $null) {
'<null>'
} elseif ($_.Value.Length -eq 0) {
'<empty>'
} else {
$_.Value -replace '\s', [Char] 0x00B7 # Middle dot
}
)
};
} `
| Out-Host
}
}
That produces the following output...
==================================================
Parsed input "@{}" to OrderedDictionary with Count = 0
==================================================
Parsed input "@{DekId=1}" to OrderedDictionary with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1 1
==================================================
Parsed input "@{DekId=1+1=2}" to OrderedDictionary with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1+1=2 1+1=2
==================================================
Parsed input "@{ }" to OrderedDictionary with Count = 0
==================================================
Parsed input "@{ DekId=1 }" to OrderedDictionary with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1 1·
==================================================
Parsed input "@{ DekId=1+1=2 }" to OrderedDictionary with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1+1=2 1+1=2·
==================================================
Parsed input "@{First=A B C;Second=X Y Z}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C;Second=X Y Z }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z·
==================================================
Parsed input "@{First=A B C;Second=X Y Z;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C;Second=X Y Z; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C; Second=X Y Z}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C; Second=X Y Z }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z·
==================================================
Parsed input "@{First=A B C; Second=X Y Z;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C; Second=X Y Z; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=;Second=X Y Z}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=;Second=X Y Z }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z·
==================================================
Parsed input "@{First=;Second=X Y Z;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=;Second=X Y Z; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=; Second=X Y Z}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=; Second=X Y Z }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z·
==================================================
Parsed input "@{First=; Second=X Y Z;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=; Second=X Y Z; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First <empty>
Second X Y Z X·Y·Z
==================================================
Parsed input "@{First=A B C;Second=}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{First=A B C;Second= }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second ·
==================================================
Parsed input "@{First=A B C;Second=;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{First=A B C;Second=; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{First=A B C; Second=}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{First=A B C; Second= }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second ·
==================================================
Parsed input "@{First=A B C; Second=;}" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{First=A B C; Second=; }" to OrderedDictionary with Count = 2
Name Value PrintableValue
---- ----- --------------
First A B C A·B·C
Second <empty>
==================================================
Parsed input "@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}" to OrderedDictionary with Count = 4
Name Value PrintableValue
---- ----- --------------
DekId <empty>
FieldId 1234 1234
OriginalValue <empty>
NewValue 1234 1234
==================================================
Parsed input "@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}" to OrderedDictionary with Count = 4
Name Value PrintableValue
---- ----- --------------
DekId <empty>
FieldId 7602 7602
OriginalValue <empty>
NewValue Alice, Hamburgler Alice,·Hamburgler
Note that due to the greedy quantifier used in the Body
group (i.e. (?<Body>.*)
), in the case when the last property has a trailing space but no trailing semicolon that space will be included in the property value. If that behavior is undesirable you can change it to a lazy quantifier (i.e. (?<Body>.*?)
).
I parsed everything into ordered hashtables/dictionaries just to make it easier to match up the input text with the output properties, but you could use a regular Hashtable
as well.
Alternatively, you can use the -replace
operator to turn your input text into valid PowerShell Hashtable
syntax by adding quotes around matched property values...
# Match the shortest text possible between "Name=" and a ";" or
# a "}" and replace it with that same text surrounded by quotes
$replacementText = $originalText -replace '(?<=[a-z]+=)(?<Value>.*?)(?=;|\s*})', '"${Value}"'
...and then use the Invoke-Expression
cmdlet to parse it into a Hashtable
instance...
$properties = Invoke-Expression -Command $replacementText
This regular expression assumes that...
;
or }
characters.Using the same input as above, the following code...
foreach ($pair in $testRecords.GetEnumerator())
{
Write-Host '=================================================='
$originalText = $pair.Value
Write-Host " Original text: $originalText"
# Match the shortest text possible between "Name=" and a ";" or
# a "}" and replace it with that same text surrounded by quotes
$replacementText = $originalText -replace '(?<=[a-z]+=)(?<Value>.*?)(?=;|\s*})', '"${Value}"'
if ([Object]::ReferenceEquals($originalText, $replacementText))
{
Write-Host 'Replacement text is indentical to original text'
}
else
{
Write-Host "Replacement text: $replacementText";
}
$properties = Invoke-Expression -Command $replacementText
Write-Host "Replacement text evaluated to $($properties.GetType().Name) with Count = $($properties.Count)"
$properties.GetEnumerator() `
| Select-Object -Property `
'Name', `
'Value', `
@{
Name = 'PrintableValue';
Expression = {
return $(
if ($_.Value -eq $null) {
'<null>'
} elseif ($_.Value.Length -eq 0) {
'<empty>'
} else {
$_.Value -replace '\s', [Char] 0x00B7 # Middle dot
}
)
};
} `
| Out-Host
}
...produces this output...
==================================================
Original text: @{}
Replacement text is indentical to original text
Replacement text evaluated to Hashtable with Count = 0
==================================================
Original text: @{DekId=1}
Replacement text: @{DekId="1"}
Replacement text evaluated to Hashtable with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1 1
==================================================
Original text: @{DekId=1+1=2}
Replacement text: @{DekId="1+1=2"}
Replacement text evaluated to Hashtable with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1+1=2 1+1=2
==================================================
Original text: @{ }
Replacement text is indentical to original text
Replacement text evaluated to Hashtable with Count = 0
==================================================
Original text: @{ DekId=1 }
Replacement text: @{ DekId="1" }
Replacement text evaluated to Hashtable with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1 1
==================================================
Original text: @{ DekId=1+1=2 }
Replacement text: @{ DekId="1+1=2" }
Replacement text evaluated to Hashtable with Count = 1
Name Value PrintableValue
---- ----- --------------
DekId 1+1=2 1+1=2
==================================================
Original text: @{First=A B C;Second=X Y Z}
Replacement text: @{First="A B C";Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second=X Y Z }
Replacement text: @{First="A B C";Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second=X Y Z;}
Replacement text: @{First="A B C";Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second=X Y Z; }
Replacement text: @{First="A B C";Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=X Y Z}
Replacement text: @{First="A B C"; Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=X Y Z }
Replacement text: @{First="A B C"; Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=X Y Z;}
Replacement text: @{First="A B C"; Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=X Y Z; }
Replacement text: @{First="A B C"; Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First A B C A·B·C
==================================================
Original text: @{First=;Second=X Y Z}
Replacement text: @{First="";Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=;Second=X Y Z }
Replacement text: @{First="";Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=;Second=X Y Z;}
Replacement text: @{First="";Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=;Second=X Y Z; }
Replacement text: @{First="";Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=; Second=X Y Z}
Replacement text: @{First=""; Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=; Second=X Y Z }
Replacement text: @{First=""; Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=; Second=X Y Z;}
Replacement text: @{First=""; Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=; Second=X Y Z; }
Replacement text: @{First=""; Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second X Y Z X·Y·Z
First <empty>
==================================================
Original text: @{First=A B C;Second=}
Replacement text: @{First="A B C";Second=""}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second= }
Replacement text: @{First="A B C";Second="" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second=;}
Replacement text: @{First="A B C";Second="";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C;Second=; }
Replacement text: @{First="A B C";Second=""; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=}
Replacement text: @{First="A B C"; Second=""}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second= }
Replacement text: @{First="A B C"; Second="" }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=;}
Replacement text: @{First="A B C"; Second="";}
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{First=A B C; Second=; }
Replacement text: @{First="A B C"; Second=""; }
Replacement text evaluated to Hashtable with Count = 2
Name Value PrintableValue
---- ----- --------------
Second <empty>
First A B C A·B·C
==================================================
Original text: @{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}
Replacement text: @{DekId=""; FieldId="1234"; OriginalValue=""; NewValue="1234"}
Replacement text evaluated to Hashtable with Count = 4
Name Value PrintableValue
---- ----- --------------
NewValue 1234 1234
OriginalValue <empty>
DekId <empty>
FieldId 1234 1234
==================================================
Original text: @{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}
Replacement text: @{DekId=""; FieldId="7602"; OriginalValue=""; NewValue="Alice, Hamburgler"}
Replacement text evaluated to Hashtable with Count = 4
Name Value PrintableValue
---- ----- --------------
NewValue Alice, Hamburgler Alice,·Hamburgler
OriginalValue <empty>
DekId <empty>
FieldId 7602 7602