Как извлечь значения из этого текста, похожего на Hashtable, в Powershell? - PullRequest
0 голосов
/ 10 марта 2020

У меня есть String в следующем формате, и мне нужна помощь для преобразования его в структуру данных, которая может легко получить доступ к парам ключ-значение внутри.

@{7068="@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}";7602="@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}";...}

Я пытался использовать ConvertFrom-String, однако я не удалось правильно манипулировать String для соответствия этому формату.

1 Ответ

2 голосов
/ 10 марта 2020

С учетом этого ввода ...

$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}";
}

... следующее использует регулярные выражения для извлечения окружающего @{ }, а затем разбивает строку для анализа того, что внутри, в [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
    }
}

Это дает следующий вывод ...

==================================================
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


Обратите внимание, что из-за жадного квантификатора 1015 *, используемого в Body group (то есть (?<Body>.*)), в случае, когда у последнего свойства есть завершающий пробел, но нет конечной точки с запятой, этот пробел будет включен в значение свойства. Если такое поведение нежелательно, вы можете изменить его на ленивый квантификатор (например, (?<Body>.*?)).

Я разбил все в упорядоченные хеш-таблицы / словари, чтобы было проще сопоставить входной текст с выходными свойствами, но вы также можете использовать обычный Hashtable.


В качестве альтернативы вы можете использовать оператор -replace , чтобы превратить введенный текст в действительный PowerShell Hashtable синтаксис , добавляя кавычки вокруг совпадающих значений свойств ...

# 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}"'

... и затем используйте Invoke-Expression командлет для его анализа в Hashtable экземпляр ...

$properties = Invoke-Expression -Command $replacementText

В этом регулярном выражении предполагается, что ...

  • Все значения свойств требуют кавычек.
  • Никакие значения свойств не содержат символов ; или }.

Используя тот же ввод, что и выше, следующий код ...

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
}

.. . производит этот вывод ...

==================================================
   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


...