0

I created powershell script which request API and get response but many times I getting null value without double quotes in JSON object which created error in other script.

How can I put double quotes where getting null value.

Response :

{ "Name" : { 
             "Tag1" : "server1",
             "Tag2" : null,
             "Tag3" : "web"
            }
}

Here, I expecting "Tag2" : "null" ( need double quotes where get null response)

Context : I'm fetching tag from azure vm, if that particular tag is not available then itz return null as expected.

Ex.
 $webvmdata = @{
 Vmtype = $wenvm.Tags.vmtype
  }
  $webvmdata | ConvertTo-Json

How can we easily handle this using powershell.

Thanks in advance.

5
  • 2
    Don't use other other people's avatars. Especially not this one. Commented Mar 23, 2022 at 14:01
  • 3
    "expecting "Tag2" : "null" (need double quotes where get null response)" - No, you don't. This case is already easily handled in PowerShell by default. You're doing something wrong. Show more context. Commented Mar 23, 2022 at 14:04
  • @Tomalak i added powershell code Commented Mar 23, 2022 at 14:21
  • 1
    So you really want null - which translates into $null when parsed in PowerShell objects with ConvertFrom-Json (which is built into Invoke-RestMethod) - to be replaced with the string "null"? Commented Mar 23, 2022 at 15:51
  • @mklement0 First, Without Double quotes null value which getting errors in python which loading. Getting "malformed string" error. So I need double quotes null value. Even used ConvertFrom-Json where also getting without double quotes for null. Commented Mar 23, 2022 at 16:29

1 Answer 1

1

Note:

  • Generally speaking, null is a legitimate JSON value that becomes $null when PowerShell parses JSON into custom objects with its ConvertFrom-Json cmdlet, which, in effect, is also built into Invoke-RestMethod.

If you really need to transform the null values into "null" values - i.e. strings - in your JSON, the most robust - but not fast - approach is to:

  • Let ConvertFrom-Json parse your JSON into custom objects...
  • ... then walk the internal structure of these objects to look for $null property values and replace them with string 'null'...
    • See the bottom section for generalized helper function Edit-LeafProperty that encapsulates this behavior.
  • ... and convert the modified objects back to JSON.
# Parse JSON into custom object(s).
$fromJson = ConvertFrom-Json @'
{
  "Name": {
    "Tag1": "server1",
    "Tag2": null,
    "Tag3": "web"
  }
}
'@

# Transform the object(s) in place.
$fromJson | ForEach-Object {
  # Helper script block that walks the object graph
  $sb = {
    foreach ($el in @($args[0])) {
      if ($el -is [System.Collections.IEnumerable]) { # Nested array
        foreach ($subEl in $el) { & $sb $subEl } # Recurse
      }
      elseif ($el -is [System.Management.Automation.PSCustomObject]) {
        foreach ($prop in $el.psobject.Properties) { 
          # If the property value is $null, replace it with string 'null'
          if ($null -eq $prop.Value) { $prop.Value = 'null' }
          elseif ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
            & $sb $prop.Value  # Recurse
          }
        } 
      }
    }
  }
  # Call the helper script block with the input object.
  & $sb $_
} 

# Convert the transformed object(s) back to JSON
ConvertTo-Json $fromJson

Output (note the "null"):

{
  "Name": {
    "Tag1": "server1",
    "Tag2": "null",
    "Tag3": "web"
  }
}

Caveat re single-element arrays:

If your input JSON is an array ([ ... ]) that happens to contain just one element, in PowerShell (Core) 7+ you need to add -NoEnumerate to the ConvertFrom-Json call in order to parse the JSON into a PowerShell array - otherwise, you'll get just get that one element itself, not wrapped in an array. (This isn't necessary in Windows PowerShell).[1]


With generalized helper function Edit-LeafProperty:

If you define the helper function below (before running the following code), the code simplifies to the following:

@'
{
  "Name": {
    "Tag1": "server1",
    "Tag2": null,
    "Tag3": "web"
  }
}
'@  | ConvertFrom-Json | 
  Edit-LeafProperty -PassThru { if ($null -eq $_.Value) { $_.Value = 'null' } } |
    ConvertTo-Json

Note:

  • Each leaf property (an object property that doesn't itself contain another, nested [pscustomobject]) is passed to the specified script block ({ ... }) bound to $_, where its .Value (and, if needed, .Name) property can be examined and updated, as needed.

  • -PassThru passes each modified object through (outputs it after modification), so that the result can directly be piped to ConvertTo-Json

    • The usual caveat re potentially unexpected truncation of the output applies: use
      -Depth as needed for object graphs more than 2 levels deep - see this post
  • The caveats re array preservation in PowerShell (Core) apply as before, with an additional twist:

    • Use ConvertFromJson -NoEnumerate to preserve (potentially nested) single-element arrays as such.
    • Because of the use of an intermediate streaming command - Edit-LeafProperty - use ConvertTo-Json -AsArray if you need to guarantee that the output JSON is an array, even when containing just one element.

Note that the function is designed to work with [pscustomobject] ([System.Management.Automation.PSCustomObject]) graphs only, such as returned by ConvertFrom-Json and ConvertFrom-Csv.

Edit-LeafProperty source code:

function Edit-LeafProperty {
  [CmdletBinding(PositionalBinding = $false)]
  param(
    [Parameter(Mandatory, Position = 0)]
    [scriptblock] $ScriptBlock,
    [Parameter(Mandatory, ValueFromPipeline)]
    [AllowNull()]
    $InputObject,
    [switch] $PassThru
  )
  begin {
    # Helper script block that walks the object graph
    $sb = {
      foreach ($el in @($args[0])) {
        if ($el -is [System.Collections.IEnumerable]) { # Nested array
          foreach ($subEl in $el) { & $sb $subEl } # Recurse
        }
        elseif ($el -is [System.Management.Automation.PSCustomObject]) {
          foreach ($prop in $el.psobject.Properties) { 
            if ($prop.Value -is [System.Management.Automation.PSCustomObject]) {
              & $sb $prop.Value # Recurse
            }
            else {
              # Invoke the leaf-property processing script block with
              # the property at hand bound to $_
              ForEach-Object $ScriptBlock -InputObject $prop
            }
          } 
        }
      }
    }
  }

  process {
    & $sb $InputObject # Walk the input object at hand.
    if ($PassThru) { $InputObject } # Pass it through, if requested.
  }

}

[1] See this answer for what prompted this change in behavior.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for sharing this. I can use it..but we can do it quick way by using powershell function or something else ?
@Nullpointer, there is no built-in function, but you can wrap the object-graph walker functionality in a custom function. Please see my update, which defines and uses generalized helper function Edit-LeafProperty.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.