Exclude the JSON property only if it exists - Powershell

2.4k views Asked by At

I have two different JSONs and I would like to remove streets from the JSON object if only it exists under Address which is an array. I am trying to do this in powershell. I can get my script working and remove the streets but I only want to run the exclude line of command if the address has the streets property.

{
    "Customer": [{
        "id": "123"
    }],
    "address": [{
            "$type": "Home",
            "name": "Houston",
            "streets": [{
                "name": "Union",
                "postalCode": "10"
            }]
        },
        {
            "$type": "Office",
            "name": "Hawai",
            "streets": [{
                "name": "Rock",
                "postalCode": "11"
            }]
        }
    ]
}

2nd JSON - Do not want to run the exclude line for 2nd JSON because there are no streets

{
    "Customer": [{
        "id": "123"
    }],
    "address": [{
            "$type": "Home",
            "name": "Houston"
        },
        {
            "$type": "Office",
            "name": "Hawai"
        }
    ]
}

Powershell script

$FileContent = Get-Content -Path "Test.json" -Raw | ConvertFrom-Json
#Only want to run for address objects that contains streets
$FileContent.address = $FileContent.address | Select-Object * -ExcludeProperty streets #Only run for 1st json and not for 2nd json
$FileContent | ConvertTo-Json

2

There are 2 answers

0
T-Me On BEST ANSWER

If you want to execute the code only if the address has the member streets you can test for just that:

if (
    ($FileContent.address | Get-Member -MemberType NoteProperty -Name "streets") -ne $null
){
    $FileContent.address = $FileContent.address | Select-Object * -ExcludeProperty streets
}
0
mklement0 On

T-Me's helpful answer is the most robust approach, because it looks for the presence of the property itself rather than non-null values.

If you're willing to assume that the absence of a value also means the absence of the property itself, you can take the following shortcut, which performs better:

$hasAtLeastOneStreet = 0 -ne
 (@((Get-Content Test.json -Raw | ConvertFrom-Json).address.streets) -ne $null).Count

.address.streets uses member-access enumeration to extract all streets values, @(...) ensures that the result is an array, -ne $null filters out any $null values from that array, and .Count counts its elements.

Note: This expression should be simpler:
$null -ne (Get-Content Test.json -Raw | ConvertFrom-Json).address.streets
but due to a bug currently cannot - see the bottom section.

To demonstrate (the input strings are compressed, single-line versions of your JSON documents):

'{"Customer":[{"id":"123"}],"address":[{"$type":"Home","name":"Houston","streets":[{"name":"Union","postalCode":"10"}]},{"$type":"Office","name":"Hawai","streets":[{"name":"Rock","postalCode":"11"}]}]}',
'{"Customer":[{"id":"123"}],"address":[{"$type":"Home","name":"Houston"},{"$type":"Office","name":"Hawai"}]}' | 
  foreach { 
    "has street values: " + 
      (0 -ne @(((ConvertFrom-Json $_).address.streets) -ne $null).Count)
  }

The above yields, showing that the first JSON document had street values, whereas the second one did not.

has street values: True
has street values: False

Note: You should be able to simplify the test expression to the following, but this doesn't work due to a bug present up to at least PowerShell 7.3.4:

# !! SHOULD work, but as of PowerShell 7.3.4 DOESN'T, due to a bug 
# !! in how [pscustomobject]s are treated in member-access enumeration.
$null -ne (Get-Content Test.json -Raw | ConvertFrom-Json).address.streets

Normally, the absence of any streets property values should result in $null, but with two or more [pscustomobject] instances present in the .address array, an array of $null values is unexpectedly returned.

See GitHub issue #13752.