skip to Main Content

I use powershell to loop through multiple Json files obtained by the REST API. But the difficulty is that there are nodes with in them, and the name of a node is not fixed, we have no way to get it, wildcards seem to have no effect.

My test Json file Workflow_Info like:

{
   "id": "/subscriptions/fcxxxx7/resourceGroups/xxxxxx/providers/Microsoft.Web/sites/xxxx/workflows/Test_email",
   "name": "xxxxxxxxx/Test_email",
   "type": "Microsoft.Web/sites/workflows",
   "kind": "Stateful",
   "location": "East Asia",
   "properties": {
      "files": {
         "Test_email/workflow.json": {
            "definition": {
               "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
               "actions": {
                  "Send_an_email_from_a_shared_mailbox_(V2)": {
                     "inputs": {
                        "host": {
                           "connection": {
                              "referenceName": "TheValueIwant"
                           }
                        },
                        "method": "post",
                        "path": "/v2/SharedMailbox/Mail"
                     },
                     "runAfter": {},
                     "type": "ApiConnection"
                  }
               },
               "contentVersion": "1.0.0.0",
               "outputs": {}
            },
            "kind": "Stateful"
         }
      }
   }
}

What I want to get is the value of the node referenceName.

But there are two difficulties:

  1. The node Test_emailworkflow.json includes the in it, how to parse it?

  2. The node Send_an_email_from_a_shared_mailbox_(V2) it is not a fixed value, it has different value in different json, I tried to use wildcard *, but not work.

My test powershell scripts is:

   $WorkflowJson = $Workflow_Info | ConvertTo-Json -depth 100
   $WorkflowJsonNew = $Workflow_Info.properties.files."$($WorkflowName)/workflow.json".definition.'*'.host.connection.referenceName | ConvertTo-Json -depth 100
   Write-Host "The workflow is:$WorkflowJsonNew"

4

Answers


  1. Chosen as BEST ANSWER

    For the first question, I have resolved it by add "" for the Test_email/workflow.json to get it:

    $WorkflowJson = $Workflow_Info | ConvertTo-Json -depth 100
    $WorkflowJsonNew = $WorkflowJson .properties.files."$($WorkflowName)/workflow.json" | ConvertTo-Json -depth 100
    

    Now I could get the value.

    But for the second question, I will open another question for some more details.


  2. First: The JSON isn’t valid on multiple places.

    => Known problem with ""

    $Workflow_Info = (Get-Content -Path "C:userdataStackoverflowout.json" -Raw) -replace "\", "\"
    

    This is a litle bit dirty. But it works in your case, because there are no further backslashes.
    At least, a clean solution has been provided from the developer of the rest service. Currently the JSON is out of standard, see https://datatracker.ietf.org/doc/html/rfc8259 => Chapter "7. Strings"

    => Row 28 => "outputs": {}, => replace the "," at the end, because the list object has been finished.
    => Row 32 => }, => replace the "," at the end, because the list object has been finished.

    To ensure a valid JSON, you should take a look here: https://jsonlint.com/
    I’m using this since years.

    Second: Your question about how to capture a unknown property name.

    # Getting the raw JSON file:
    $Local:Workflow_Info_AsJsonString = (Get-Content -Path "C:userdataStackoverflowout.json" -Raw) -replace "\", "\"
    # Converting the JsonString to a PSObject
    $Local:Workflow_Info_AsPSObject   = ConvertFrom-Json -InputObject $Workflow_Info_AsJsonString
    # Getting the name of the property with the changing name.  (Preconditioned there is only one object under "actions".
    $Local:PropertyWithChangingName    = ($Workflow_Info_AsPSObject.properties.files.'Test_emailworkflow.json'.definition.actions).PSObject.Properties.Name
    # Accessing a property behind the property with the changing name
    $Workflow_Info_AsPSObject.properties.files.'Test_emailworkflow.json'.definition.actions.($PropertyWithChangingName).inputs.path  
    

    Please excuse my bad English. Computer languages are easier to learn as human languages. Regards from Bavaria.

    Login or Signup to reply.
  3. The second question is a duplicate of:
    (But as it has a bounty, it can be closed as such)

    And can best resolved with the following function:

     function Get-Node {
      [CmdletBinding()][OutputType([Object[]])] param(
        [ScriptBlock]$Where,
        [AllowNull()][Parameter(ValueFromPipeLine = $True, Mandatory = $True)]$InputObject,
        [Int]$Depth = 10
      )
      process {
        if ($_ -isnot [String] -and $Depth -gt 0) {
          if ($_ -is [Collections.IDictionary]) {
            if (& $Where) { $_ }
            $_.get_Values() | Get-Node -Where $Where -Depth ($Depth - 1)
          }
          elseif ($_ -is [Collections.IEnumerable]) {
            for ($i = 0; $i -lt $_.get_Count(); $i++) { $_[$i] | Get-Node -Where $Where -Depth ($Depth - 1) }
          }
          elseif ($Nodes = $_.PSObject.Properties.Where{ $_.MemberType -eq 'NoteProperty' }) {
            $Nodes.ForEach{
              if (& $Where) { $_ }
              $_.Value | Get-Node -Where $Where -Depth ($Depth - 1)
            }
          }
        }
      }
    }
    

    Usage

    Finding node(s) with a specific name and value (-format):

    $Node = $Workflow_Info.properties.files.'Test_email/workflow.json'.definition.actions |Get-Node -Where { $_.Name -eq 'referenceName' }
    $Node.Value
    TheValueIwant
    

    Replacing the value of the found node(s):

    $Node |ForEach-Object {
        $_.Value  = 'My new value for: ' + $_.Value
    }
    $Workflow_Info |ConvertTo-Json -Depth 15
    

    Results

    {
      "id": "/subscriptions/fcxxxx7/resourceGroups/xxxxxx/providers/Microsoft.Web/sites/xxxx/workflows/Test_email",
      "name": "xxxxxxxxx/Test_email",
      "type": "Microsoft.Web/sites/workflows",
      "kind": "Stateful",
      "location": "East Asia",
      "properties": {
    "files": {
      "Test_email/workflow.json": {
        "definition": {
          "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
          "actions": {
            "Send_an_email_from_a_shared_mailbox_(V2)": {
              "inputs": {
                "host": {
                  "connection": {
                    "referenceName": "My new value for: TheValueIwant"
                  }
                },
                "method": "post",
                "path": "/v2/SharedMailbox/Mail"
              },
              "runAfter": {},
              "type": "ApiConnection"
            }
          },
          "contentVersion": "1.0.0.0",
          "outputs": {}
        },
        "kind": "Stateful"
      }
    }
      }
    }
    Login or Signup to reply.
  4. After your edits to the question that changed to /, your first question no longer applies, and your own answer is no longer needed.

    • The only thing worth noting with respect to your first question is that you need quoting in order to access properties whose names contain unusual characters such as and /, as already shown in the code in your question:
      • $Workflow_Info.properties.files."$($WorkflowName)/workflow.json"

        • Note that it can be simplified to $Workflow_Info.properties.files."$WorkflowName/workflow.json", given that you don’t strictly need $(...) inside expandable (double-quoted) string ("...") for stand-alone variable references such as $WorkflowName – however, you do need $(...) for expressions, such as $Workflows[0] or $Workflow.Name
      • For verbatim names with unusual names, use verbatim (single-quoted) strings ('...'), e.g.. ([pscustomobject] @{ 'foobar' = 42 }).'foobar'


    As for your second question:

    A simplified example:

    # Get all .Parent property values, if any, from the values of all
    # properties that the System.IO.FileInfo instance returned by Get-ChildItem has.
    (Get-ChildItem $PROFILE).psobject.Properties.Value.Parent
    
    • Note the psobject.Properties.Value part, which enumerates all
      property values and looks for a .Parent property on each.

      • That is, it is the equivalent of combining the results of (Get-ChildItem $PROFILE).Name, (Get-ChildItem $PROFILE).Length, …, over all properties that a FileInfo instance has.
    • Situationally, there may be NO results ($null), ONE result, or an ARRAY of results.

      • In the case at hand, only the DirectoryInfo instances stored in the .Directory property has a .Parent property, so that a single result – another DirectoryInfo instance representing the parent directory of the directory in which the $PROFILE file is located – is returned.

      • By contrast, if you used .Name, you’d get three results, because the objects stored in the .PSDrive, .PSProvider, and .Directory properties all have a .Name property.

    • Note that it is possible to use multiple .psobject.Properties.Value expressions in a single property path, one for each level of the object-graph hierarchy where the property name(s) are unknown.

      • However, you always need to know at what level of the hierarchy the target value is to be found and provide the appropriate number of path components.

      • If not known, you need a solution that walks an entire object graph (hierarchy of nested objects) to look for properties of interest, as shown in iRon’s answer.

    Applied to your case (this is a self-contained example that can be run as-is):

    $fromJson = ConvertFrom-Json @'
    {"id":"/subscriptions/fcxxxx7/resourceGroups/xxxxxx/providers/Microsoft.Web/sites/xxxx/workflows/Test_email","name":"xxxxxxxxx/Test_email","type":"Microsoft.Web/sites/workflows","kind":"Stateful","location":"East Asia","properties":{"files":{"Test_email/workflow.json":{"definition":{"$schema":"https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#","actions":{"Send_an_email_from_a_shared_mailbox_(V2)":{"inputs":{"host":{"connection":{"referenceName":"TheValueIwant"}},"method":"post","path":"/v2/SharedMailbox/Mail"},"runAfter":{},"type":"ApiConnection"}},"contentVersion":"1.0.0.0","outputs":{}},"kind":"Stateful"}}}}
    '@
    
    $WorkflowName = 'Test_email'
    
    # Again, note the `psobject.Properties.Value` part.
    # -> 'TheValueIwant'
    $fromJson.properties.files."$WorkflowName/workflow.json".definition.actions.
      psobject.Properties.Value.inputs.host.connection.referenceName
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search