Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 9 Min.

Schnell konvertiert

JSON-Daten kann die PowerShell oft mit weniger Codezeilen verarbeiten als C#/VB.
© dotnetpro
Die PowerShell unterstützt die JavaScript Object Notation (JSON) seit 2012 in Version 3.0. Damals hieß die Power­Shell allerdings noch „Windows PowerShell“. Die Windows PowerShell (auf Basis von .NET Framework) hat Microsoft dann auf dem Versionsstand 5.1 eingefroren. 2018 kam neu die PowerShell Core 6.0 (auf Basis von .NET Core) heraus, davon ist Version 6.2 (2019) aktuell. Ende des Jahres soll dann die PowerShell 7.0 (auf Basis von .NET Core 3.0) als gemeinsamer Nachfolger von Windows PowerShell 5.1 und Power­Shell Core 6.2 erscheinen. Das PowerShell-Entwicklungsteam ist damit Vorreiter beim Zusammenführen von klassischer Produktlinie und Core-Produktlinie. In vergleichbarer Weise wird das .NET-Entwicklungsteam das .NET Framework und .NET Core Ende 2020 zu „.NET 5.0“ vereinen.

ConvertFrom-JSON

Ein mächtiges PowerShell-Cmdlet für die JSON-Verarbeitung ist ConvertFrom-JSON; ein anderes ist ConvertTo-JSON. Das erste erzeugt aus einer JSON-Datenstruktur in einer Zeichenkette einen Objektbaum, das zweite serialisiert umgekehrt einen Objektbaum zu Text im JSON-Format.Die JSON-Datenstruktur in Listing 1 beschreibt das Berufsleben des Autors dieses Beitrags. Diese JSON-Daten lassen sich mit Get-Content laden und mit einem in der Pipeline folgenden ConvertFrom-JSON in ein dynamisches .NET-Objekt verwandeln:
Listing 1: Beispieldaten im JSON-Format
{ <br/>  <span class="hljs-attr">"LetzteÄnderung"</span>: <span class="hljs-string">"2019-07-15T15:04:05.000+0100"</span>, <br/>  <span class="hljs-attr">"Anrede"</span>: <span class="hljs-string">"Herr"</span>, <br/>  <span class="hljs-attr">"Titel"</span>: <span class="hljs-string">"Dr."</span>, <br/>  <span class="hljs-attr">"Name"</span>: <span class="hljs-string">"Holger Schwichtenberg"</span>, <br/>  <span class="hljs-attr">"PLZ"</span>: <span class="hljs-number">45257</span>, <br/>  <span class="hljs-attr">"Ort/Land"</span>: <span class="hljs-string">"Essen/Deutschland"</span>, <br/>  <span class="hljs-attr">"Ist MVP"</span>: <span class="hljs-literal">true</span>, <br/>  <span class="hljs-attr">"Themen"</span> : [<span class="hljs-string">".NET"</span>, <span class="hljs-string">".NET Core"</span>, <span class="hljs-string">"Visual Studio"</span>,<br/>    <span class="hljs-string">"Softwarearchitektur"</span>, <span class="hljs-string">"Verteilte Systeme"</span>,<br/>    <span class="hljs-string">"DevOps"</span>, <span class="hljs-string">"PowerShell"</span>], <br/>  <span class="hljs-attr">"Firmen"</span> : [ <br/>    { <br/>      <span class="hljs-attr">"Name"</span>:<br/>        <span class="hljs-string">"5Minds IT-Solutions Gmbh & Co KG"</span>, <br/>      <span class="hljs-attr">"Firmensitz"</span>: <span class="hljs-string">"Gelsenkirchen"</span>, <br/>      <span class="hljs-attr">"Website"</span>: <span class="hljs-string">"www.5Minds.de"</span>, <br/>      <span class="hljs-attr">"Tätigkeitsgebiete"</span>:<span class="hljs-string">"Softwareentwicklung"</span> <br/>    }, <br/>    { <br/>      <span class="hljs-attr">"Name"</span>: <span class="hljs-string">"www.IT-Visions.de"</span>, <br/>      <span class="hljs-attr">"Firmensitz"</span>: <span class="hljs-string">"Essen"</span>, <br/>      <span class="hljs-attr">"Website"</span>: <span class="hljs-string">"www.IT-Visions.de"</span>, <br/>      <span class="hljs-attr">"Tätigkeitsgebiete"</span>: [ <span class="hljs-string">"Beratung"</span>,<br/>        <span class="hljs-string">"Schulung"</span> ] <br/>    }, <br/>    { <br/>      <span class="hljs-attr">"Name"</span>: <span class="hljs-string">"Ebner Media Group GmbH & Co. KG"</span>, <br/>      <span class="hljs-attr">"Firmensitz"</span>: <span class="hljs-string">"Ulm"</span>, <br/>      <span class="hljs-attr">"Website"</span>: <span class="hljs-string">"www.dotnetpro.de"</span>, <br/>      <span class="hljs-attr">"Tätigkeitsgebiete"</span>:<br/>        [<span class="hljs-string">"Fachinformationen"</span>] <br/>    } <br/>  ] <br/>} 
Get-Content
  X:<span class="hljs-symbol">\D</span>okumente<span class="hljs-symbol">\J</span>SON<span class="hljs-symbol">\P</span>erson.json | ConvertFrom-JSON 
Bild 1 zeigt, dass dies bereits zu einer strukturierten Ausgabe in der PowerShell führt. Ein Pipe-Zeichen nach Get-Member beweist, dass der Pipeline-Inhalt nicht aus Zeichenketten besteht, sondern ein Objekt vom Typ System.Management.Automation.PSCustomObject ist, das die Elemente des JSON-Wurzelknotens nun als dynamische Eigenschaften enthält (siehe die NoteProperty-Ausgaben in Bild 1).
ConvertFrom-JSONin Aktion auf dem JSON-Dokument in Listing 1(Bild 1) © Autor
Folglich kann man über dieses Objekt nun auf die Daten aus dem JSON-Dokument zugreifen, zum Beispiel $p.Name. Dabei ist – wie meistens in der PowerShell – die Groß- und Kleinschreibung nicht von Bedeutung; $p.name funktioniert ebenso.Für die Eigenschaftsnamen des dynamischen Objekts bekommt der Nutzer auch Eingabehilfen: Das PowerShell Integrated Scrip­ting Environment (ISE), Visual Studio mit Po­werShell Tools und auch Visual Studio Code mit PowerShell-Extension [1] unterstützen die Eingabe mit IntelliSense, siehe Bild 2 – allerdings nur unter der Voraussetzung, dass die JSON-Struktur bereits einmal konvertiert wurde und die Variable, die das dabei entstandene Objektmodell enthält, nach Skriptende weiterhin gefüllt ist.
IntelliSense-Eingabeunterstützungin Visual Studio Code mit der kostenfreien Erweiterung PowerShell von Microsoft(Bild 2) © Autor
Wie in Bild 2 zu erkennen ist, muss also der folgender Aufruf von Get-Content zumindest einmal erfolgt sein, um die Eingabehilfe genießen zu können:
<span class="hljs-variable">$p</span> = <span class="hljs-built_in">Get-Content</span>
  X:\Dokumente\JSON\Person.json
  | <span class="hljs-built_in">ConvertFrom-JSON</span> 
Auch die PowerShell-Konsole (einschließlich der in Visual Studio und Visual Studio Code enthaltenen Varianten) hilft bei der Eingabe von Property-Namen; allerdings hier nicht im IntelliSense-Stil, sondern per Tabulatortaste, die durch die Vorschläge blättert.Was allerdings nicht funktioniert, ist folgende Aufspaltung der obigen Pipeline:
<span class="hljs-variable">$pfad</span> = <span class="hljs-string">"X:\Dokumente\JSON\Person.json"</span> 
<span class="hljs-variable">$dateiinhalt</span> = <span class="hljs-built_in">Get-Content</span> <span class="hljs-variable">$pfad</span> 
<span class="hljs-built_in">ConvertFrom-JSON</span> <span class="hljs-variable">$dateiinhalt</span> 
Dies beantwortet die PowerShell nur mit der Fehlermeldung „ConvertFrom-JSON : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'InputObject'“.Das liegt daran, dass das Cmdlet Get-Content den Inhalt ­einer Datei immer zeilenweise in einzelne Zeichenketten aufspaltet. Die Variable $dateiinhalt enthält nicht nur eine Zeichenkette, sondern ein Array von Zeichenketten. Mit diesem Array als Eingabeparameter kommt ConvertFrom-JSON aber nicht klar.Der Nutzer kann jedoch die zeilenweise Aufspaltung bei Get-Content mit dem Zusatzparameter -raw verhindern:
<span class="hljs-variable">$pfad</span> = <span class="hljs-string">"X:\Dokumente\JSON\Person.json"</span> 
<span class="hljs-variable">$dateiinhalt</span> = <span class="hljs-built_in">Get-Content</span> <span class="hljs-variable">$pfad</span> -raw 
<span class="hljs-built_in">ConvertFrom-JSON</span> <span class="hljs-variable">$dateiinhalt</span> 
Ohne den Parameter -raw gibt es noch die Möglichkeit, das Zeichenketten-Array per Pipeline zu ConvertFrom-JSON zu senden. Die einzelnen Zeilen wird das Cmdlet dann als ein JSON-Dokument interpretieren:
<span class="hljs-variable">$pfad</span> = <span class="hljs-string">"X:\Dokumente\JSON\Person.json"</span> 
<span class="hljs-variable">$dateiinhalt</span> = <span class="hljs-built_in">Get-Content</span> <span class="hljs-variable">$pfad</span> 
<span class="hljs-variable">$dateiinhalt</span> | <span class="hljs-built_in">ConvertFrom-JSON</span> 

Sonderzeichen

Wie Bild 1 zeigt, erstellt die PowerShell in dem dynamischen Objekt Eigenschaften mit der gleichen Schreibweise wie die Felder in der JSON-Datei. Eine kleine Herausforderung stellen die JSON-Felder Ort/Land und Ist MVP dar, denn diese sind keine erlaubten Namen für Eigenschaften in .NET und sind auch nicht in PowerShell-Ausdrücken erlaubt.Hier sind beim Zugriff auf die Eigenschaft einfache oder doppelte Anführungszeichen zu verwenden:
$p.<span class="hljs-string">'Ort/Land'</span> 
$p.<span class="hljs-string">"Ort/Land"</span> 
Nachfolgende Aufrufe von Eigenschaften können dann aber wieder normal geschrieben werden, zum Beispiel:
<span class="hljs-variable">$p</span>.<span class="hljs-string">'Ort/Land'</span><span class="hljs-selector-class">.Length</span> 
<span class="hljs-variable">$p</span>.<span class="hljs-string">"Ort/Land"</span>.Length 
Gleiches gilt für Ist MVP, hier in einem Beispiel mit Write-Host und Bedingung:
<span class="hljs-built_in">Write-Host</span> <span class="hljs-string">"MVP-Status?"</span> 
<span class="hljs-built_in">Write-Host</span> $(<span class="hljs-keyword">if</span> (<span class="hljs-variable">$p</span>.<span class="hljs-string">'Ist MVP'</span>) { <span class="hljs-string">"Ja"</span> } <span class="hljs-keyword">else</span> { <span class="hljs-string">"Nein"</span> }) 

Datentypen

Bild 1 zeigt, dass ConvertFrom-JSON die Datentypen im Fall des JSON-Dokuments in Listing 1 richtig erkennt:
  • LetzteÄnderung wird als DateTime-Datentyp verstanden.
  • PLZ wird als long verstanden.
  • Ist MVP wird als bool verstanden.
Dementsprechend lassen sich die Daten weiterverarbeiten. Beispielsweise ist es dann möglich, das Jahr aus dem Feld LetzteÄnderung zu ermitteln:
<span class="hljs-keyword">Write</span>-Host <span class="hljs-string">"Zuletzt geändert im Jahr:"</span> 
<span class="hljs-keyword">Write</span>-Host <span class="hljs-built_in">$p</span>.LetzteÄnderung.Year 
Das Erkennen von Datumsformaten funktioniert aber erst seit PowerShell Core 6.0. Die Windows PowerShell lieferte immer nur eine Zeichenkette, die dann manuell konvertiert werden musste [2]. Auch andere Detailunterschiede sind möglich, denn die Windows PowerShell 1.0 bis 5.1 basiert auf dem .NET Framework und den dort enthaltenen alten JSON-(De-)Serializer im Namensraum System.Json. PowerShell Core 6.x und PowerShell 7.0 dagegen bauen auf .NET Core auf. Dort gibt es diesen Namensraum aber nicht mehr. Hier wird Newtonsoft JSON (alias JSON.NET) verwendet [3]. Offen ist, wie das PowerShell-Team bei PowerShell 7.0 verfahren wird, denn Microsoft wird dort ja Newtonsoft JSON durch eine eigene Implementierung (System.Text.Json) ablösen [4].Die geladene Themenliste ist ein Array von Zeichenketten, das mit $p.Themen angesprochen werden kann; die Power­Shell gibt die Liste aus, siehe Bild 3. Hingegen ist $p.Firmen ein Array von Objekten, das die PowerShell als Tabelle ausgibt, wie ebenfalls in Bild 3 zu sehen ist.
Die Mengenmagieder PowerShell(Bild 3) © Autor
Diese Standardausgabe muss der PowerShell-Nutzer aber nicht hinnehmen. Er kann zur Auswertung alle PowerShell-Cmdlets verwenden, sie zum Beispiel mit Where-Object filtern, mit Sort-Object sortieren und die Ausgaben mit einem der Format-Cmdlets, etwa Format-List, formatieren:
<span class="hljs-variable">$p</span>.Firmen | <span class="hljs-built_in">Where-Object</span> { <span class="hljs-variable">$_</span>.name <span class="hljs-nomarkup">-like</span> <span class="hljs-string">"*gmbh*"</span> }
  | <span class="hljs-built_in">Sort-Object</span> Firmensitz
  | <span class="hljs-built_in">Format-List</span> Name, Firmensitz 
Der Anwender kann die Ausgabe auch in einem anderen Format speichern, beispielsweise mit Export-Csv:
<span class="hljs-variable">$p</span>.Firmen | <span class="hljs-built_in">Where-Object</span> { <span class="hljs-variable">$_</span>.name <span class="hljs-nomarkup">-like</span> <span class="hljs-string">"*gmbh*"</span> }
  | <span class="hljs-built_in">Sort-Object</span> Firmensitz
  | <span class="hljs-built_in">Export-Csv</span> X:\Dokumente\JSON\Firmen.csv 

Objekt-Magie

Spannend aus der Sicht der Objektorientierung ist ein Power­Shell-Ausdruck wie dieser:
<span class="hljs-variable">$p</span><span class="hljs-selector-class">.Firmen</span><span class="hljs-selector-class">.Name</span> 
In C++, C#, Java und den meisten anderen objektorientierten Sprachen würde dieser Ausdruck den Compiler oder Interpreter zu einer Fehlermeldung veranlassen, denn $p.Firmen ist ja laut Bild 1 ein Array von Objekten. Das Array an sich besitzt aber keine Eigenschaft Name, sondern nur die einzelnen im Array enthaltenen Objekte verfügen über Name. In „normalen“ objektorientierten Programmiersprachen müsste erst einmal ein Element der Liste angesprochen werden:
<span class="hljs-variable">$p</span><span class="hljs-selector-class">.Firmen</span>[<span class="hljs-number">0</span>]<span class="hljs-selector-class">.Name</span> oder <span class="hljs-variable">$p</span><span class="hljs-selector-class">.Firmen</span>[<span class="hljs-number">1</span>].Name 
Die PowerShell abstrahiert aber an vielen Stellen von den tatsächlichen Objektstrukturen und leitet .Name an die einzelnen Elemente der Menge weiter, sodass $p.Firmen[0].Name tatsächlich eine Liste aller Namen ausgibt.Noch interessanter ist dieser Aufruf:
<span class="hljs-variable">$p</span><span class="hljs-selector-class">.Firmen</span><span class="hljs-selector-class">.T</span>ätigkeitsgebiete 
Wie man in der JSON-Datei in Listing 1 sieht, verhält sie sich bei ”Tätigkeitsgebiete” inkonsequent: Bei der ersten Firma ist ein einzelner Eintrag als Zeichenkette hinterlegt, bei der zweiten ein Array mit zwei Zeichenketten und beim dritten Unternehmen ein Array mit einer Zeichenkette.Dies sollte der Autor eines JSON-Dokuments natürlich vermeiden, allerdings lehrt die Praxis: So etwas kommt immer mal wieder vor und bringt streng typisierte Sprachen wie C# in Not.Die PowerShell mit ihrer dynamischen Typisierung steckt die verschieden aufgebaute Objekte einfach in die Menge Firmen, wie in Bild 4 zu sehen ist. Beim Einsatz von Get-Member (abgekürzt gm) sieht das dann so aus:
Die Objektesind verschieden aufgebaut(Bild 4) © Autor
  • Bei $p.Firmen | Get-Member bekommt man die Metadaten zu dem ersten Objekt in der Liste, das eine Zeichenkette bei Tätigkeitsgebiete besitzt.
  • $p.Firmen[0] | Get-Member ist das Gleiche wie $p.Firmen | Get-Member.
  • $p.Firmen[1] | Get-Member liefert hingegen das zweite Element, und dessen Metadaten zeigen nun bei Tätigkeitsgebiete ein Objekt-Array an.
Beim Aufruf $p.Firmen.Tätigkeitsgebiete gleicht die Power­Shell diese Unterschiede aber dann wieder aus. Bild 3 zeigt: Das Ergebnis ist eine Liste aller Tätigkeitsgebiete aus allen Firmen, egal ob diese als Zeichenkette oder Array von Zeichenketten gespeichert sind.

JSON-Daten von Web-APIs

In der Praxis kommen JSON-Daten oft bei REST-basierten Webdiensten (alias Web APIs) ins Spiel. Auch mit diesen kann die PowerShell umgehen. Das Cmdlet Invoke-WebRequest löst einen HTTP-Aufruf aus und gibt ein Objekt vom Typ Microsoft.PowerShell.Commands.HtmlWebResponseObject zurück. Darin ist der Inhalt in der Eigenschaft RawContent enthalten.Die folgenden drei Befehlszeilen liefern vom REST-Web-API von www.nuget.org eine Liste aller NuGet-Pakete, die das Wort „Math“ im Namen tragen:
<span class="hljs-variable">$json</span> = Invoke-WebRequest
  <span class="hljs-string">"https://api-v2v3search-0.nuget.org/query?q=Math"</span> 
<span class="hljs-variable">$daten</span> = ConvertFrom-JSON <span class="hljs-variable">$json</span><span class="hljs-selector-class">.RawContent</span> 
<span class="hljs-variable">$daten</span><span class="hljs-selector-class">.Data</span><span class="hljs-selector-class">.Title</span> 
Es funktioniert auch ohne expliziten Zugriff auf RawContent. ConvertFrom-JSON erwartet eine Zeichenkette, und wenn die PowerShell das Objekt vom Typ Microsoft.PowerShell.Commands.Html­WebRes­ponseObject anliefert, wird sie automatisch die Methode ToString() aufrufen. Diese Methode liefert das Gleiche wie RawContent:
<span class="hljs-variable">$json</span> = Invoke-WebRequest
  <span class="hljs-string">"https://api-v2v3search-0.nuget.org/</span>
<span class="hljs-string">  query?q=Math"</span> 
<span class="hljs-variable">$daten</span> = ConvertFrom-JSON <span class="hljs-variable">$json</span> 
<span class="hljs-variable">$daten</span><span class="hljs-selector-class">.Data</span><span class="hljs-selector-class">.Title</span> 

JSON-Daten aus Kommandozeilen­werkzeugen

Auch andere Kommandozeilenbefehle können JSON liefert, zum Beispiel das Azure-DevOps-Kommandozeilenwerkzeug (Azure DevOps CLI). Mit ihm lassen sich Microsofts Cloud-Dienst Azure Dev­Ops Services und der lokale Azure DevOps Server (früher Team Foundation Server) per Kommandozeile steuern. Leider gibt es für die administra­tiven Aufgaben rund um Azure DevOps keine ­PowerShell-Cmdlets, die Objekte direkt liefern würden. Daher müssen die JSON-Ausgaben des Azure DevOps CLI verarbeitet werden.Nachdem das Azure CLI und die Azure Dev­Ops-Erweiterungen dafür installiert wurden und Sie sich bei Azure DevOps angemeldet sowie die dazu nötigen Angaben zu Organisation und Projekt konfiguriert haben [5], können Sie Befehle wie diesen abschicken, der eine Liste aller Work-Items in der persönlich definierten Abfrage Nicht behobene Fehler liefert:
<span class="hljs-symbol">az</span> <span class="hljs-keyword">boards </span>query
  --path <span class="hljs-string">"my queries/Nicht behobene Fehler"</span> 
Die Ausgabe erfolgt üblicherweise im JSON-Format. Nur wenn Sie das Standardausgabeformat geändert haben, ist expli­zit zu ergänzen, dass JSON gewünscht ist:
<span class="hljs-comment">az</span> <span class="hljs-comment">boards</span> <span class="hljs-comment">query</span>
<span class="hljs-comment"> </span> <span class="hljs-literal">-</span><span class="hljs-literal">-</span><span class="hljs-comment">path</span> <span class="hljs-comment">"my</span> <span class="hljs-comment">queries/Nicht</span> <span class="hljs-comment">behobene</span> <span class="hljs-comment">Fehler“</span>
<span class="hljs-comment"> </span> <span class="hljs-literal">-</span><span class="hljs-literal">-</span><span class="hljs-comment">output</span> <span class="hljs-comment">json</span> 
Das Ergebnis können Sie in der PowerShell an ConvertFrom-JSON senden:
<span class="hljs-string">az </span><span class="hljs-string">boards </span><span class="hljs-string">query</span>
<span class="hljs-string"> </span> <span class="hljs-built_in">--path</span> <span class="hljs-string">"my queries/Nicht behobene Fehler"</span>
  <span class="hljs-built_in">--output</span> <span class="hljs-string">json </span>| <span class="hljs-string">ConvertFrom-JSON</span> 
Nun lässt sich das Ergebnis strukturiert auswerten:
(az boards query
  --path <span class="hljs-string">"my queries/Nicht behobene Fehler"</span>
  --<span class="hljs-keyword">output</span> json | ConvertFrom-JSON).Fields)
  | <span class="hljs-built_in">Where</span>-Object { $_.'<span class="hljs-keyword">System</span>.<span class="hljs-built_in">Title</span>' -like ‚*Eingabe*'}
  | <span class="hljs-keyword">Format</span>-Table '<span class="hljs-keyword">System</span>.ID','<span class="hljs-keyword">System</span>.<span class="hljs-built_in">Title</span>',
  '<span class="hljs-keyword">System</span>.State' 

Probleme mit Mengen an der Wurzel

Leider gibt es ein Problem bei ConvertFrom-JSON, wenn das Wurzelelement des JSON-Dokuments kein einzelnes Objekt ({ … }), sondern ein Array ([ … ]) ist. Der nachstehende Befehl sollte die Liste der definierten Azure-DevOps-Pipelines liefern, deren Name „Client“ enthält. Das Ergebnis sollte nach dem Feld Name sortiert in einer Tabelle ausgegeben werden:
az pipelines list -o JSON | <span class="hljs-type">ConvertFrom</span>-JSON
  | <span class="hljs-type">Where</span>-Object { $<span class="hljs-keyword">_</span>.name -notlike <span class="hljs-string">"*Client*"</span> }
  | <span class="hljs-type">Sort</span>-Object name | <span class="hljs-type">Format</span>-<span class="hljs-keyword">Table</span> name, url 
Allerdings verwundert, dass zwar die Tabellenausgabe mit den zwei gewünschten Spalten, aber weder der Filter mit dem Cmdlet Where-Object noch die Sortierung mit Sort-Object wirken. Das liegt am Cmdlet ConvertFrom-JSON, bei dem das PowerShell-Entwicklerteam bei der Implementierung gravierend von den Standards abgewichen ist. Falls sich an der Wurzel der JSON-Datei ein Array befindet, dann legt das Cmdlet das Array in die Pipeline. Normalerweise entpacken aber Cmdlets ein Array in Einzelobjekte und legen diese Einzelobjekte in die Pipeline.Microsoft hat dieses Problem leider bis heute nicht behoben [6] – das Verhalten nachträglich zu ändern würde bestehende Skripte zu anderen Ergebnissen kommen lassen. Eine Umgehung wäre, einen neuen Parameter einzuführen. Den aber gibt es noch nicht.Die aktuell verfügbare Umgehung dieses Problems ist, entweder ein zusätzliches Foreach-Object in die Pipeline zu setzen:
az pipelines list -o JSON | <span class="hljs-type">ConvertFrom</span>-JSON
  | <span class="hljs-type">Foreach</span>-Object { $<span class="hljs-keyword">_</span>}
  | <span class="hljs-type">Where</span>-Object { $<span class="hljs-keyword">_</span>.name -notlike <span class="hljs-string">"*Client*"</span> }
  | <span class="hljs-type">Sort</span>-Object name | <span class="hljs-type">Format</span>-<span class="hljs-keyword">Table</span> name, url 
Eine Alternative ist, den vorderen Teil mit dem Aufruf von az und ConvertFrom-JSON in runde Klammern zu setzen:
(az pipelines list -o JSON | <span class="hljs-built_in">ConvertFrom-JSON</span>)
  | <span class="hljs-built_in">Where-Object</span> { <span class="hljs-variable">$_</span>.name <span class="hljs-nomarkup">-notlike</span> <span class="hljs-string">"*Client*"</span> }
  | <span class="hljs-built_in">Sort-Object</span> name| <span class="hljs-built_in">format-table</span> name, url 

Test-JSON

Seit PowerShell Core 6.1 gibt es auch ein drittes eingebautes Cmdlet mit „JSON“ im Namen: Test-JSON. Mit diesem Cmdlet lässt sich prüfen, ob eine Zeichenkette ein gültiges JSON-Dokument enthält. Allerdings gibt es hier wieder die Besonderheit, dass das JSON-Dokument bei diesem Cmdlet nicht zeilenweise per Pipeline ankommen darf. Die folgende Befehlsfolge funktioniert also nicht:
Get-Content X:<span class="hljs-symbol">\D</span>okumente<span class="hljs-symbol">\J</span>SON<span class="hljs-symbol">\P</span>erson.json | Test-JSON 
Vielmehr ist bei Get-Content explizit der Parameter -raw zu verwenden:
Get-Content X:<span class="hljs-symbol">\D</span>okumente<span class="hljs-symbol">\J</span>SON<span class="hljs-symbol">\P</span>erson.json -raw
  | Test-JSON 
Mit Test-JSON ist auch eine Validierung gegen ein JSON-Schema-Dokument [7] möglich:
<span class="hljs-variable">$doc</span> = Get-Content X:\Dokumente\JSON\Person<span class="hljs-selector-class">.json</span> -raw 
<span class="hljs-variable">$schema</span> = Get-Content
  X:\Dokumente\JSON\Person<span class="hljs-selector-class">.schema</span><span class="hljs-selector-class">.json</span> -raw 
Test-JSON -Json <span class="hljs-variable">$doc</span> -Schema <span class="hljs-variable">$schema</span> 
Im Erfolgsfall liefert Test-JSON ein lapidares true, wie es bei allen Test-Cmdlets in der PowerShell üblich ist. Bei einem Fehler kommt zusätzlich zum false eine Fehlermeldungsliste zurück. In Bild 5 bemängelt Test-JSON, dass bei der ersten Firma in Listing 1 die Tätigkeitsgebiete nicht aus einem Array, sondern nur aus einer einfachen Zeichenkette bestehen.
Test-JSONin Aktion(Bild 5) © Autor

JSON-Strukturen erzeugen

Natürlich gibt es auch einen Gegenspieler zu ConvertFrom-JSON mit Namen ConvertTo-JSON. Der folgende Befehl speichert die Liste der laufenden Windows-Systemdienste (die das Cmdlet als Objekt vom Typ System.ServiceProcess.ServiceController liefert) als JSON-Dokument:
<span class="hljs-built_in">Get-Service</span> | <span class="hljs-built_in">Where-Object</span> status <span class="hljs-nomarkup">-eq</span> <span class="hljs-string">"Running"</span>
  | <span class="hljs-built_in">ConvertTo-Json</span> -Depth <span class="hljs-number">3</span> | <span class="hljs-built_in">Out-File</span> t:\dienste.json 
Dabei wird eine Serialisierungstiefe für den Objektbaum von 3 angegeben; der voreingestellte Wert ist 2. Hier ist 2 mindestens notwendig, um die Namen der abhängigen Dienste zu erhalten. Bei Tiefe 1 ergibt sich die Liste in Listing 2.
Listing 2: Das Ergebnis bei einer Serialisierungstiefe von 1
{ &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"CanPauseAndContinue"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;false&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"CanShutdown"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;true&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"CanStop"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;true&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"DisplayName"&lt;/span&gt;:  &lt;span class="hljs-string"&gt;"DHCP Client"&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"DependentServices"&lt;/span&gt;:  [ &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt; &lt;br/&gt;  ], &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"MachineName"&lt;/span&gt;:  &lt;span class="hljs-string"&gt;"."&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"ServiceName"&lt;/span&gt;:  &lt;span class="hljs-string"&gt;"DHCP"&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"ServicesDependedOn"&lt;/span&gt;:  [ &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt; &lt;br/&gt;  ], &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"ServiceHandle"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;null&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"Status"&lt;/span&gt;:  &lt;span class="hljs-number"&gt;4&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"ServiceType"&lt;/span&gt;:  &lt;span class="hljs-number"&gt;48&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"StartType"&lt;/span&gt;:  &lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"Site"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;null&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"Container"&lt;/span&gt;:  &lt;span class="hljs-literal"&gt;null&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"Name"&lt;/span&gt;:  &lt;span class="hljs-string"&gt;"DHCP"&lt;/span&gt;, &lt;br/&gt;  &lt;span class="hljs-attr"&gt;"RequiredServices"&lt;/span&gt;:  [ &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt;, &lt;br/&gt;    &lt;span class="hljs-string"&gt;"System.ServiceProcess.ServiceController"&lt;/span&gt; &lt;br/&gt;  ] &lt;br/&gt;} 
Mit Tiefe 2 ergeben sich dann statt System.ServiceProcess.ServiceController die Eigenschaften der Dienste inklusive der Namen der referenzierten anderen Systemdienste.

Fazit

Die JSON-Serialisierung und -Deserialisierung in der Power­Shell machen Spaß, denn es sind nur wenige Tastenanschläge nötig. Die Inkonsistenz bei der Mengenverarbeitung im Cmdlet ConvertFrom-JSON ist aber lästig, weil man die Klammern leicht vergessen kann und es dann unbemerkt bleiben kann, dass die definierten Filter nicht wirken. Auch dass Test-JSON anders als ConvertFrom-JSON zeilenweise Eingaben nicht unterstützt, ist nicht schön. Auch hier zeigt sich, dass verschiedene Softwareentwickler am Werk waren und die Qualitätssicherung besser sein könnte.

Fussnoten

  1. Visual Studio Market Place, PowerShell Language ­Support for Visual Studio Code, http://www.dotnetpro.de/­SL1911DataAccess1
  2. GitHub: PowerShell, ConvertFrom-JSON breaking change: automagically detects timestamps and ­deserializes to DateTime #3378, http://www.dotnetpro.de/­SL1911DataAccess2
  3. Json.NET, http://www.newtonsoft.com/json
  4. NuGet, System.Text.Json, http://www.dotnetpro.de/­SL1911DataAccess3
  5. Microsoft, Installieren des Azure CLI, http://www.dotnetpro.de/SL1911DataAccess4
  6. GitHub: PowerShell, ConvertFrom-JSON sends objects converted from a JSON array as an *array* through the pipeline #3424, http://www.dotnetpro.de/SL1911DataAccess5
  7. JSON Schema, https://json-schema.org

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
IoT neu eingebunden - Integration und Verwaltung von IoT-Geräten mit Azure IoT Operations
Wie sich das neue Azure IoT Operations von bestehenden Azure-Diensten unterscheidet, welche Technologien dabei zum Einsatz kommen und wann sich der Umstieg lohnt.
16 Minuten
15. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

Erste Schritte: Milvus Vector DB in .NET - Microsoft
Luis Quintanilla, Program Manager in Microsofts Developer Division, stellt das Milvus .NET SDK vor, das derzeit als Vorschau-Version erhältlich ist.
2 Minuten
14. Mär 2024
Kostenloses Tool zum Zeichnen von ER-Diagrammen - dbdiagram.io
Auf dbdiagram.io steht im Web ein kostenloses Tool zum Erzeugen von Entity-Relationship-Diagrammen bereit.
2 Minuten
11. Mai 2022
Ein Buchhaltungsprogramm entwickeln - MySQL
Seit über zehn Jahren arbeitet der litauische Hobby-Entwickler mit CodeProject-Namen "Niemand25" an der Open-Source-Finanzbuchhaltungssoftware Apskaita5 für litauische Buchhalter. Jetzt schreibt er die App von Grund auf neu und dokumentiert sie als Artikelserie.
2 Minuten
13. Aug 2019
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige