Search code examples
powershellparsinghashmaphashtablestring-conversion

How can I extract the values from this Hashtable-like text in Powershell?


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.


Solution

  • 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...

    • All property values require quoting.
    • No property values contain the ; 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