Trouble parsing string to object with PowerShell

匆匆过客 提交于 2021-02-08 07:33:27

问题


I have a string with structured data (see below). I need to take this string and convert it to an object, so I can export it to .csv (or whatever else is requested of me). I ran the following code:

$data = $string -replace "\s*:\s*","="

But my output looks like this:

City=Country=Department=DisplayName=John Doe
DistinguishedName=CN=John Doe, CN=Users, DC=domain, DC=com
EmailAddress=jdoe@domain.com
Enabled=False
Fax=GivenName=John
MobilePhone=Name=John Doe
ObjectClass=user
ObjectGUID=cdb9a45c-80f4-4919-bf43-5db8d9ca83da
Office=OfficePhone=PostalCode=SamAccountName=jdoe
SID=S-1-5-21-2025429266-2000478354-1606980848-16934
State=StreetAddress=Surname=Doe
Title=UserPrincipalName=jdoe@domain.com

This is clearly not correct. What is a better way to make this conversion? I thought about using ConvertFrom-String with the TemplateContent parameter, but haven't been able to make that work yet.

Here are the first two entries in the string (which contains several users worth of data):

$string = @"
City              :
Country           :
Department        :
DisplayName       : John Doe
DistinguishedName : CN=John Doe,CN=Users,DC=domain,DC=com
EmailAddress      : jdoe@domain.com
Enabled           : False
Fax               :
GivenName         : John
MobilePhone       :
Name              : John Doe
ObjectClass       : user
ObjectGUID        : cdb9a45c-80f4-4919-bf43-5db8d9ca83da
Office            :
OfficePhone       :
PostalCode        :
SamAccountName    : jdoe
SID               : S-1-5-21-2025429266-2000478354-1606980848-16934
State             :
StreetAddress     :
Surname           : Doe
Title             :
UserPrincipalName : jdoe@domain.com

City              :
Country           :
Department        :
DisplayName       : DiscoverySearchMailbox{D919BA15-46A6-415f-80AD-7E09334BB852}
DistinguishedName : CN=DiscoverySearchMailbox {D919BA15-46A6-415f-80AD-7E09334BB852},CN=Users,DC=domain,DC=com
EmailAddress      : DiscoverySearchMailbox{D919BA15-46A6-415f-80AD-7E09334BB852}@domain.com
Enabled           : False
Fax               :
GivenName         :
MobilePhone       :
Name              : DiscoverySearchMailbox{D919BA15-46A6-415f-80AD-7E09334BB852}
ObjectClass       : user
ObjectGUID        : 0f35137a-de93-472f-9114-5488a462d178
Office            :
OfficePhone       :
PostalCode        :
SamAccountName    : SM_2187102a90634829b
SID               : S-1-5-21-2438891277-1009865731-3229889747-3109
State             :
StreetAddress     :
Surname           : MsExchDiscoveryMailbox D919BA15-46A6-415f-80AD-7E09334BB852
Title             :
UserPrincipalName : DiscoverySearchMailbox{D919BA15-46A6-415f-80AD-7E09334BB852}@domain.com
"@

Thanks.


回答1:


If:

  • you can rely on values never containing : themselves
  • you don't mind that the properties of the resulting custom objects don't reflect the input order (though you could easily, but inefficiently, correct that with piping to a Select-Object call enumerating the properties explicitly),

you can use ConvertFrom-StringData (I suggest avoiding the finicky and poorly documented ConvertFrom-String):

$string.Trim() -split '(?m)(?=^City\b)' -ne '' | ForEach-Object { 
  [pscustomobject] ($_ -replace ':', '=' | ConvertFrom-StringData)
}  # | Export-Csv ....

Note: Casting to [pscustomobject] requires PSv3+; on PSv2, use New-Object PSCustomObject -Property (...)

  • $string.Trim() -split '(?m)(?=^City\b)' -ne '' splits the input lines into blocks of lines each representing one object; splitting is performed by lines that start with City; -ne '' filters out the empty block that results from parsing the start of the input.

    • .Trim() is needed to ignore empty lines at the start of the string.
  • $_ -replace ':', '=' | ConvertFrom-StringData converts each block into
    <key>=<value> lines that ConvertFrom-StringData converts as a group to a [hashtable] instance; because hash tables inherently enumerate their entries in no guaranteed order, this is where the input ordering of properties is lost.

  • Cast [pscustomobject] converts each hashtable to a custom object, which is implicitly output; the output can be piped to Export-Csv.




回答2:


Here You go:)

    $a=@"
City              :
Country           :
Department        :
DisplayName       : John Doe
DistinguishedName : CN=John Doe,CN=Users,DC=domain,DC=com
EmailAddress      : jdoe@domain.com
Enabled           : False
Fax               :
GivenName         : John
MobilePhone       :
Name              : John Doe
ObjectClass       : user
ObjectGUID        : cdb9a45c-80f4-4919-bf43-5db8d9ca83da
Office            :
OfficePhone       :
PostalCode        :
SamAccountName    : jdoe
SID               : S-1-5-21-2025429266-2000478354-1606980848-16934
State             :
StreetAddress     :
Surname           : Doe
Title             :
UserPrincipalName : jdoe@domain.com
"@
$b=ConvertFrom-Csv -InputObject $a -Delimiter ':' -Header "key","value"
$c=New-Object -TypeName System.Management.Automation.PSObject 
$b|%{ $c|Add-Member -NotePropertyName $_.key -NotePropertyValue "$($_.value)"}

Resulting object looks like this

PS C:\Users\Tomasz> $c|gm




  S C:\Users\Tomasz> $c


City               : 
Country            : 
Department         : 
DisplayName        : John Doe
DistinguishedName  : CN=John Doe,CN=Users,DC=domain,DC=com
EmailAddress       : jdoe@domain.com
Enabled            : False
Fax                : 
GivenName          : John
MobilePhone        : 
Name               : John Doe
ObjectClass        : user
ObjectGUID         : cdb9a45c-80f4-4919-bf43-5db8d9ca83da
Office             : 
OfficePhone        : 
PostalCode         : 
SamAccountName     : jdoe
SID                : S-1-5-21-2025429266-2000478354-1606980848-16934
State              : 
StreetAddress      : 
Surname            : Doe
Title              : 
UserPrincipalName  : jdoe@domain.com

If this kind of solution seems like a good Idea I'll work on my answer more.
It obviously needs white spaces removal and some nicer variable names, but I trust You can get that done Yourself :)




回答3:


The escape sequence \s matches all whitespace, including newlines. Because of that lines without a value are actually merged with the next line. Split the string at newlines, do the replacement, then merge the string array back to a single string.

$data = $string -split '\r?\n' -replace '\s*:\s*','=' | Out-String

or make sure you don't replace line break characters:

$data = $string -replace '[\t ]*:[\t ]*', '='

Edit:

Since your input data seems to consist of multiple records, not just one, you need to split the resulting string by record, so that you have individual strings per data set. Convert each data set to a hashtable with ConvertFrom-StringData, then convert those hashtables to custom objects.

$data = $string -split '(?<=\r?\n)\r?\n' | ForEach-Object {
    $prop = $_.Trim() -split '\r?\n' -replace '\s*:\s*','=' |
            Out-String |
            ConvertFrom-StringData
    New-Object -Type PSObject -Property $prop
}

In PowerShell v3 and newer you can use the [PSCustomObject] type accelerator instead of New-Object:

$data = $string -split '(?<=\r?\n)\r?\n' | ForEach-Object {
    $prop = $_.Trim() -split '\r?\n' -replace '\s*:\s*','=' |
            Out-String |
            ConvertFrom-StringData
    [PSCustomObject]$prop
}

The resulting list of objects can then be exported to a CSV.



来源:https://stackoverflow.com/questions/51602171/trouble-parsing-string-to-object-with-powershell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!