web-dev-qa-db-fra.com

Itération via un fichier JSON PowerShell

J'essaie de parcourir le fichier JSON ci-dessous dans PowerShell.

Sans nommer spécifiquement les balises supérieures (par exemple 17443 et 17444), comme je ne les connais pas à l'avance, je ne peux pas trouver un moyen de parcourir les données.

Je veux sortir les tags 3, 4 et 5 (titre, prénom, nom) pour tous les enregistrements.

Comment pourrais-je accomplir cela?

{
   "17443":{
      "sid":"17443",
      "nid":"7728",
      "submitted":"1436175407",
      "data":{
         "3":{
            "value":[
               "Mr"
            ]
         },
         "4":{
            "value":[
               "Jack"
            ]
         },
         "5":{
            "value":[
               "Cawles"
            ]
         }
      },
      "17444":{
         "sid":"17444",
         "nid":"7728",
         "submitted":"1436891400",
         "data":{
            "3":{
               "value":[
                  "Miss"
               ]
            },
            "4":{
               "value":[
                  "Charlotte"
               ]
            },
            "5":{
               "value":[
                  "Tann"
               ]
            }
         }
      },
      "17445":{
         "sid":"17445",
         "nid":"7728",
         "submitted":"1437142325",
         "data":{
            "3":{
               "value":[
                  "Mr"
               ]
            },
            "4":{
               "value":[
                  "John"
               ]
            },
            "5":{
               "value":[
                  "Brokland"
               ]
            }
         }
      }
   }
}

Je peux accéder aux données avec le code ci-dessous, mais je veux éviter de mettre 17443, 17444, etc.

$data = ConvertFrom-Json $json

foreach ($i in $data.17443)
{
   foreach ($t in $i.data.3)
   {
      Write-Host $t.value
   }
   foreach ($t in $i.data.4)
   {
      Write-Host $t.value
   }
   foreach ($t in $i.data.5)
   {
      Write-Host $t.value
   }
}
24
Omen9876

PowerShell 3.0+

Dans PowerShell 3.0 et versions supérieures (voir: Déterminer la version PowerShell installée ), vous pouvez utiliser l'applet de commande ConvertFrom-Json Pour convertir une chaîne JSON en une structure de données PowerShell.

C'est pratique et malheureux à la fois - pratique, car il est très facile de consommer JSON, malheureux parce que ConvertFrom-Json Vous donne PSCustomObjects , et ils sont difficiles à répéter en tant que paires clé-valeur .

Dans ce JSON particulier, les clés semblent être dynamiques/inconnues à l'avance, comme "17443" Ou "17444". Cela signifie que nous avons besoin de quelque chose qui puisse transformer un PSCustomObject en une liste de valeurs-clés que foreach peut comprendre.

# helper to turn PSCustomObject into a list of key/value pairs
function Get-ObjectMembers {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [PSCustomObject]$obj
    )
    $obj | Get-Member -MemberType NoteProperty | ForEach-Object {
        $key = $_.Name
        [PSCustomObject]@{Key = $key; Value = $obj."$key"}
    }
}

Nous pouvons maintenant parcourir le graphe d'objets et produire une liste d'objets de sortie avec Title, FirstName et LastName

$json = '{"17443": {"17444": {"sid": "17444","nid": "7728","submitted": "1436891400","data": {"3": {"value": ["Miss"]},"4": {"value": ["Charlotte"]},"5": {"value": ["Tann"]}}},"17445": {"sid": "17445","nid": "7728","submitted": "1437142325","data": {"3": {"value": ["Mr"]},"4": {"value": ["John"]},"5": {"value": ["Brokland"]}}},"sid": "17443","nid": "7728","submitted": "1436175407","data": {"3": {"value": ["Mr"]},"4": {"value": ["Jack"]},"5": {"value": ["Cawles"]}}}}'

$json | ConvertFrom-Json | Get-ObjectMembers | foreach {
    $_.Value | Get-ObjectMembers | where Key -match "^\d+$" | foreach {
        [PSCustomObject]@{
            Title = $_.value.data."3".value | select -First 1
            FirstName = $_.Value.data."4".value | select -First 1
            LastName = $_.Value.data."5".value | select -First 1
        }
    }
}

Sortie

 Titre Prénom Nom 
 ----- --------- -------- 
 Mlle Charlotte Tann 
 M. John Brokland 

PowerShell 2.0/Approche alternative

Une approche alternative qui fonctionne également pour PowerShell 2.0 (qui ne prend pas en charge certaines des constructions ci-dessus) impliquerait d'utiliser le .NET classe JavaScriptSerializer pour gérer le JSON:

Add-Type -AssemblyName System.Web.Extensions
$JS = New-Object System.Web.Script.Serialization.JavaScriptSerializer

Maintenant, nous pouvons faire une opération très similaire, même un peu plus simple que ci-dessus, car JavaScriptSerializer vous donne régulièrement Dictionnaires , qui sont faciles à répéter sous forme de paires clé-valeur via la GetEnumerator() méthode:

$json = '{"17443": {"17444": {"sid": "17444","nid": "7728","submitted": "1436891400","data": {"3": {"value": ["Miss"]},"4": {"value": ["Charlotte"]},"5": {"value": ["Tann"]}}},"17445": {"sid": "17445","nid": "7728","submitted": "1437142325","data": {"3": {"value": ["Mr"]},"4": {"value": ["John"]},"5": {"value": ["Brokland"]}}},"sid": "17443","nid": "7728","submitted": "1436175407","data": {"3": {"value": ["Mr"]},"4": {"value": ["Jack"]},"5": {"value": ["Cawles"]}}}}'

$data = $JS.DeserializeObject($json)

$data.GetEnumerator() | foreach {
    $_.Value.GetEnumerator() | where { $_.Key -match "^\d+$" } | foreach {
        New-Object PSObject -Property @{
            Title = $_.Value.data."3".value | select -First 1
            FirstName = $_.Value.data."4".value | select -First 1
            LastName = $_.Value.data."5".value | select -First 1
        }
    }
}

La sortie est la même:

 Titre Prénom Nom 
 ----- --------- -------- 
 Mlle Charlotte Tann 
 M. John Brokland 

Si votre JSON est supérieur à 4 Mo, définissez la JavaScriptSerializer.MaxJsonLength Propriété en conséquence.


Lors de la lecture de JSON à partir de fichiers

Si vous lisez un fichier, utilisez Get-Content -Raw -Encoding UTF-8.

  • -Raw Car sinon Get-Content Renvoie un tableau de lignes individuelles et JavaScriptSerializer.DeserializeObject Ne peut pas gérer cela. Les versions récentes de Powershell semblent avoir amélioré la conversion de type pour les arguments de la fonction .NET, il est donc possible que cela ne génère pas d'erreur sur votre système, mais si c'est le cas (ou simplement pour être sûr), utilisez -Raw.
  • -Encoding Car il est judicieux de spécifier l'encodage d'un fichier texte lorsque vous le lisez et UTF-8 Est la valeur la plus probable pour les fichiers JSON.

Remarques

  • ConvertFrom-Json() vous donne un objet personnalisé PowerShell (PSCustomObject) qui reflète les données dans la chaîne JSON.
  • Vous pouvez parcourir les propriétés d'un objet personnalisé avec Get-Member -type NoteProperty
  • Vous pouvez accéder dynamiquement aux propriétés d'un objet à l'aide de la syntaxe $object."$propName", Alternativement $object."$(some PS expression)".
  • Vous pouvez créer votre propre objet personnalisé et l'initialiser avec un tas de propriétés avec New-Object PSObject -Property @{...}, Alternativement [PSCustomObject]@{ .. } `
37
Tomalak