Home > Mobile >  Serialization differences between PowerShell's Format-List and ConvertTo-Json
Serialization differences between PowerShell's Format-List and ConvertTo-Json

Time:11-18

I'm trying to parse the results of PowerShell's Get-NetIPConfiguration in Python.

The results contain the values I want in the default formatting (Format-List), but not when converted to JSON, which is the format I would like to consume.

Note how DNSServer is serialized by Format-List:

PS C:\Users\BoppreH> Get-NetIPConfiguration | Format-List
InterfaceAlias       : VirtualBox Host-Only Network
InterfaceIndex       : 23
InterfaceDescription : VirtualBox Host-Only Ethernet Adapter
IPv4Address          : 192.168.56.1
IPv6DefaultGateway   :
IPv4DefaultGateway   :
DNSServer            : fec0:0:0:ffff::1
                       fec0:0:0:ffff::2
                       fec0:0:0:ffff::3

[...]

while ConvertTo-Json -Depth 1 serializes the DNSServer attribute differently (in a completely useless way):

PS C:\Users\BoppreH> Get-NetIPConfiguration | ConvertTo-Json -Depth 1
[
    {
        "Detailed":  false,
        "ComputerName":  "BOPPREH-DESKTOP",
        "InterfaceAlias":  "VirtualBox Host-Only Network",
        "InterfaceIndex":  23,
        "InterfaceDescription":  "VirtualBox Host-Only Ethernet Adapter",
        "CompartmentId":  1,
        "NetAdapter":  "MSFT_NetAdapter (CreationClassName = \"MSFT_NetAdapter\", DeviceID = \"{EAF79493-7C78-44D2-ADB4-F3EF196D2F49}\", SystemCreationClassName = \"CIM_NetworkPort\", SystemName = \"boppreh-desktop\")",
        "NetCompartment":  "MSFT_NetCompartment (InstanceID = \";55;\")",
        "NetIPv6Interface":  "MSFT_NetIPInterface (Name = \"??55??55;\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"\")",
        "NetIPv4Interface":  "MSFT_NetIPInterface (Name = \"??55?55;\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"\")",
        "NetProfile":  null,
        "AllIPAddresses":  "192.168.56.1 fe80::d83f:9609:86ff:2b57#",
        "IPv6Address":  "",
        "IPv6TemporaryAddress":  "",
        "IPv6LinkLocalAddress":  "fe80::d83f:9609:86ff:2b57#",
        "IPv4Address":  "192.168.56.1",
        "IPv6DefaultGateway":  null,
        "IPv4DefaultGateway":  null,
        "DNSServer":  "MSFT_DNSClientServerAddress (Name = \"23\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"23\") MSFT_DNSClientServerAddress (Name = \"23\", CreationClassName = \"\", SystemCreationClassName = \"\", SystemName = \"2\")"
    },
[...]

It's not until depth level 4 that the addresses become visible, but by then the output is several times larger and much harder to navigate.

My current alternative is to pipe the results in Select-Object and use calculated properties to convert the values myself (in the case of DNSServer it's $_.DNSServer.ServerAddresses -join " "), but this is cumbersome to do for each property and makes other properties serialize differently too.

How can I force the JSON serializer to format values like the list formatter?

CodePudding user response:

Note how DNSServer is serialized by Format-List

The Format-* cmdlets do not serialize, they produce for-display string representations, using PowerShell's output-formatting system (as opposed to its serialization infrastructure).

These representations are not meant to be used for programmatic processing, but if you do want to process them as strings, you can pipe them to Out-String:

# Returns a single-line string; add -Stream to get an array of lines.
# Add -Width to explicitly specify a line width (console window width is the default).
# Note: Since Format-List is used for formatting *by default*, 
#       you don't strictly need the Format-List here.
#       Alternatively, use Format-Table for a *table* representation
$stringRep = Get-NetIPConfiguration | Format-List | Out-String

How can I force the JSON serializer to format values like the list formatter?

Your only option is indeed to simplify the object graph by constructing your own [pscustomobject] instances, such as via Select-Object and calculated properties, or in a ForEach-Object loop with [pscustomobject] literals (e.g.
[pscustomobject] @{ foo = 'bar'; baz = 'quux' }).

For instance:

Get-NetIPConfiguration | ForEach-Object {
  [pscustomobject] @{
    InterfaceAlias = $_.InterfaceAlias
    InterfaceIndex = $_.InterfaceIndex
    InterfaceDescription = $_.InterfaceDescription
    'NetProfile.Name' = $_.NetProfile.Name
    IPv4Address = $_.IPv4Address -join "`n"
    IPv6DefaultGateway = $_.IPv6DefaultGateway.NextHop
    IPv4DefaultGateway = $_.IPv4DefaultGateway.NextHop
  }
}

Piping the above to ConvertTo-Json gives a reasonable representation.

Do note that Get-NetIPConfiguration's formatting data has additional logic built in that varies the output fields in its for-display representations by adapter type, which the above doesn't take into account.

  • Related