Powershell und JSON

Die lange Vorherrschaft von XML-Dateien (Siehe XML und XSLT) wird mehr und mehr durch JSON abgelöst. Am Anfang hatte ich noch meine Probleme mit den geschweiften und eckigen Klammern und einer passenden Formatierung. Aber mittlerweile ziehe ich JSON immer XML vor, wenn es irgendwie geht.

JSON mit PowerShell

PowerShell selbst kennt erst mal kein "JSON-Objekt". Bei XML gibt es ja "[xml]" als Variablentyp aber es gibt kein [json]-Prefix. Die in einem JSON-Objekt hinterlegten Felder und Werte werden aber zu einem PSCustomObject, wenn wie diese einlesen und konvertieren.

Die beiden Seiten enthalten sehr viele Beispiele, die unterschiedliche Objekte entsprechend wandeln und da möchte ich nicht weiter drauf eingehen. Sie werden nur bei den ein oder anderen Skripten von mir sicher vermehrt die JSON-Nutzung sehen. Zumal JSON auch bei der Graph API und vielen anderen Stellen genutzt wird.

Typumwandung

Auf ein besonderes Problem mit JSON bin ich aber bei der Umsetzung meine Agenten Wallbox3Agent gestoßen. Auch hier hole ich mir Daten per Invoke-RESTMethod (REST - Representational State Transfer) und bekomme eine JSON-Struktur zurück. (Siehe auch Tesla Wallbox Gen 3) Dabei habe ich z.B. den Wert "vehicle_connected" ausgelesen, der den Wert "true" oder "false" hat.

Ich bin dann erst einmal davon ausgegangen, dass das PowerShell-Objekt nach der Konvertierung ein "String" mit dem Wert "true" oder "false" ist und ich entsprechend vergleiche. das hat aber nicht funktioniert, wie folgende kleine Testserien gezeigt hat.

Wert Code Ergebnis
true
('{"test":true}' |ConvertFrom-Json).test.gettype().name
boolean

Das Ergebnis ist ein Boolean und der Wert ist "$true"

"True"
('{"test":"True"}' |ConvertFrom-Json).test.gettype().name
String

Durch die doppelten Anführungsstriche konvertiert PowerShell den Wert in einen "String"

'True'
('{"test":'True'}' |ConvertFrom-Json).test.gettype().name
String

Durch die einfachen Anführungsstriche konvertiert PowerShell den Wert in einen "String"

True
('{"test":True}' |ConvertFrom-Json).test.gettype().name
ConvertFrom-Json: Conversion from JSON failed with error: 
Unexpected character encountered while parsing value: T. Path 'test'

Interessanterweise ist JSON hier Case-sensibel, denn ein True wird nicht erkannt.

Das gleiche Bild ergibt sich mit false, "false", 'false' und False.

Damit war natürlich mein Interesse geweckt und ich habe mir eine JSON-Struktur gebaut, die String, Integer, Float, Boolean und Array erhält.

$jsonstring = '{
   "boolean": false,
   "unsignedinteger": 3300,
   "signedinteger": -3300,
   "float": 219.5,
   "String": "OK",
   "Array": [
                "a1",
                "a2"
            ],
   "recursion": {
                "r1": 2222,
                "r2": "r2value"
                }
}'

Mit der ConvertFrom-Json erhalte ich dann:

$jsonobject = $jsonstring | ConvertFrom-Json
$jsonobject

boolean         : False
unsignedinteger : 3300
signedinteger   : -3300
float           : 219,5
String          : OK
Array           : {a1, a2}
recursion       : @{r1=2222; r2=r2value}

Es ist gut zu erkennen, dass das Array zu einem PowerShell-Array konvertiert wurde und auch die Rekursion quasi zu einem Unterobjekt wurde. Wer genau hinschaut sieht aber schon, dass False anders geschrieben sind und auch das Dezimalzeichen bei "float" verändert wurde.  Was es genau ist, liefert ein "Get-Member":

$jsonobject | gm  -MemberType noteproperty

   TypeName: System.Management.Automation.PSCustomObject

Name            MemberType   Definition
----            ----------   ----------
Array           NoteProperty Object[] Array=System.Object[]
boolean         NoteProperty bool boolean=False
float           NoteProperty double float=219,5
recursion       NoteProperty System.Management.Automation.PSCustomObject recursion=@{r1=2222; r2=r2value}
signedinteger   NoteProperty long signedinteger=-3300
String          NoteProperty string String=OK
unsignedinteger NoteProperty long unsignedinteger=3300


$jsonobject.recursion | gm -MemberType noteproperty

   TypeName: System.Management.Automation.PSCustomObject

Name MemberType   Definition
---- ----------   ----------
r1   NoteProperty long r1=2222
r2   NoteProperty string r2=r2value

Damit erklärt sich auch, dass ich bei einen "String-Vergleich" die "falsche" Antwort bekomme:

PS C:\> $jsonobject.boolean -eq "false"
False

PS C:\> $jsonobject.contactor_closed -eq $false
True

Weitere Links