Seite 1 von 17

JSON-APIs auswerten

Verfasst: 20.07.2021, 12:45
von MichaelN
JSON-Ausgabe von Web-APIs auswerten

Jetzt habe ich mal den ganz großen Wurf gemacht und mein JSON-Skript soweit erweitert, das es auch beliebige, komplexere, valide JSON-Ausgaben von Web-Seiten / APIs auswerten kann. Und zwar vollautomatisch ohne Anpassungen im Code, mit auszählen von Textstellen oder ähnlichem. Der einzige Nachteil ist, das dadurch die anzulegenden Systemvariablen durch den im JSON vorgegebenen Namen bestimmt sind.
Da die Probleme mit system.exec und intensiver String-Verarbeitung und begrenzten Variablennamen mit einer aktuellen Firmware längst Geschichte sind, ist es nun möglich sowas ohne Sonderlösungen in der Skriptsprache der CCU abzubilden.

Eine alternative Idee JSON Daten zu erlangen: siehe Beitrag von Henke

Was macht das Skript?
API eines externen Servers abfragen, den JSON-Output decodieren und in vorhandene Systemvariablen schreiben

benötigte Fähigkeiten:
Grundkenntnisse in der WebUI-Programmierung (Anlegen und Ändern von Programmen, Einfügen und Anpassen von Skripten)
Anlegen von Systemvariablen
Skripte unter "Skript testen" laufen lassen und die Ausgaben verstehen und intepretieren
grundlegendes Logik-Verständnis / Abstraktionsvermögen um die beschriebenen Lösungen ggf. auf eigene Bedürfnisse anzupassen

Systemgrenzen: Arrays funktionieren, Unter-Objekte funktionieren, Objekte geschachtelt in Arrays funktionieren, ebenso Arrays in Gruppen. Allerdings funktioniert nur eine Verschachtelungsebene sicher. Mehrfach geschachtelte Objekte werden u. U. nicht vollständig gelesen.
Wenn die in JSON genutzten Zeichen zur Strukturierung ":,[]{}" in den Nutzdaten vorkommen werden sie durch Leerzeichen ersetzt (einstellbar durch anpassen der Variable "durch"). Unterstriche '_' werden durch Bindestriche '-' ersetzt. Um Arrays getrennt von Objekten zu behandeln ersetze ich in Arrays das Komma ',' durch Backslash '\'. Die ggf in den Nutzdaten vorhandenen \ werden zu / umgeschrieben. Das kann ggf. durch Änderung der Variablen TrennerArray und TrennerArrayErsatz angepasst werden, wenn man unbedingt das \ in den Daten benötigt.


Damit es keine Kollision mit vorhandenen Systemvariablen (SV) gibt, wird dem SV-Namen ein Präfix vorangestellt. Der Präfix kann nach eigenen Wünschen und Bedürfnissen angepasst werden - siehe im Skript "prefix = ...". Nur dieses Präfix und die URL der abzurufenden Web-Adresse sind im Skript zu ändern. Das sind diese Stellen:

Code: Alles auswählen

string url = "http://IP/";
string prefix = "JSON_";
V1.9: will man statt der System-Funktion ein CUxD-Device nutzen, dann ist dieses Gerät entsprechend in die Variable einzutragen:

Code: Alles auswählen

string CUXD_Device = "CUxD.CUX2801001:1";
V1.11: Falls die Ausgabe des URL-Aufrufs vor oder nach dem dem eigentlichen JSON noch unnötige Daten enthält, kann man diese entfernen lassen. Dazu in diesen Zeilen den Text eingeben, mit dem der JSON-Code beginnen soll (Start_JSON) und/oder bis zu dem das JSON gehen soll (Ende_JSON). Dies kann die Ausführung bei sehr umfangreichen JSON-Daten stark beschleunigen.

Code: Alles auswählen

! optional kann man Texte am Anfang und Ende abschneiden
string Start_JSON =''; ! 'Hier den Start des eigentlichen JSON-Codes eingeben'
string Ende_JSON =''; ! 'Hier den Ende des eigentlichen JSON-Codes eingeben'  
Beispiel siehe viewtopic.php?f=18&t=68762&start=60#p705892

V1.12: Auf vielfachen Wunsch kann das Skript nun auch Systemvariablen mit Wunsch-Namen befüllen. Man ist also nicht mehr auf die teilweise kryptischen automatisch generierten SV-Namen angewiesen. Details dazu siehe viewtopic.php?f=18&t=68762&p=802546#p802546
Weil sich dadurch die Laufzeit verlängern kann lasse ich auch die Version 1.11 ohne diese Option online stehen

Das Skript enthält Testdaten. D.h. wenn man das Skript unverändert in "Skript testen" laufen lässt, erhält man auf jeden Fall eine Ausgabe. Die Testdaten (Bereich zwischen !Testdaten und ! Ende Testdaten) sollte man nach dem ersten Testlauf rauslöschen, damit das Skript bei Fehlern in der Datenübermittlung richtig reagieren kann.

Für die Werte, die man aus dem JSON benötigt, muss man eine Systemvariable mit dem passenden Namen anlegen: bestehend aus Präfix+JSON-Schlüsselname. Bei komplexeren JSON-Daten mit Unter-Objekten und Arrays setzen sich die SV-Namen dann aus Objektname+JSON-Schlüssel+Arrayname+fortlaufender Nummer zusammen.

Am besten Testen unter Programme / Zentralenverknüpfungen / Skript testen.
Dann erhält man eine Ausgabe, an der man sehen kann, wie die Systemvariablen heißen müssen und welche Werte da abgelegt werden. Wenn die SV existiert, dann wird im Erfolgsfall === SV xxx written === ausgegeben.

Wenn es da läuft in ein Programm einbinden.

Hier der Skript-Code:

Code: Alles auswählen

!JSON universell auslesen
!V1.12 MichaelN https://homematic-forum.de/forum/viewtopic.php?f=18&t=68762
!GPL-3.0-or-later
string url = "http://IP/";
url = "wget --no-check-certificate --timeout=2 -O - '"#url#"'";
string prefix = "JSON_";
string CUXD_Device = ""; ! falls man CUxD  nutzt, Device eintragen z.B. "CUxD.CUX2801001:1"
string Name_original = ""; ! durch Semikolon getrennte SV-Namen eintragen wie sie vom Skript ermittelt werden
string Name_neu = ""; ! durch Semikolon getrennte SV-Namen eintragen die stattdessen genutzt werden sollen
! optional kann man Texte am Anfang und Ende abschneiden
string Start_JSON =''; ! 'Hier den Start des eigentlichen JSON-Codes eingeben'
string Ende_JSON =''; ! 'Hier den Ende des eigentlichen JSON-Codes eingeben'
! Trenner kann notfalls angepasst werden, wenn es mit den Nutzdaten kollidiert
string TrennerArray = " \ ";  TrennerArray = TrennerArray.Trim();
string TrennerArrayErsatz = "/";
! festlegen welche Zeichen in den Nutzdaten durch welches Zeichen ersetzt werden
string ersetzen = ":,[]{}";
string durch = " ";

string rueck;
string error;
object SV;
string SVName;
string SVName_kpl;
var Wert;
string itemID; integer i = 0;

!Testdaten rauslöschen für produktiven Betrieb
rueck = '{
  "Vorname": "Ronald",
  "Nachname": "Reagan",
  "Geburtsdatum": "1911-02-06",
  "Nationalitaet": "US-amerikanisch",
  "Partei":
  {
    "Name": "Republican Party",
    "Synonyme": ["Republikaner","Grand Old Party", "GOP"],
    "Hauptsitz": "Washington/D.C.",
    "Gründungsdatum": "1854-03-20",
    "Gründungsort": "Ripon"
  },
  "Amt": "US-Präsident",
  "Hobbys": ["Reiten", "Golfen", "Lesen"],
  "verheiratet": true,
  "Kinder": [
    {
      "Vorname": "Michael",
      "Nachname": "Reagan"    },
   {
      "Vorname": "Patti",
      "Nachname": "Davis"    },
   {
      "Vorname": "Ron",
      "Nachname": "Reagan"    }]
}
';
! Ende Testdaten


!- Abruf der Daten
if ( !CUXD_Device ) {
! system.exec
system.Exec(url , &rueck, &error);
} else {
! CUxD
dom.GetObject(CUXD_Device#".CMD_SETS").State(url);
dom.GetObject(CUXD_Device#".CMD_QUERY_RET").State(1);
rueck = dom.GetObject(CUXD_Device#".CMD_RETS").State();
}



if (!rueck== "")
{
   WriteLine("=== HTTP-Request OK === "#rueck);
   if ( Start_JSON.Length() > 0 ) {
       ! JSON am Anfang kürzen
	   rueck = rueck.Substr(rueck.Find(Start_JSON));
   }
   if ( Ende_JSON.Length() > 0 ) {
       ! JSON am Ende kürzen
	   rueck = rueck.Substr(0,rueck.Find(Ende_JSON));
   }
   rueck = rueck.ToLatin(); ! wegen deutschen Umlauten
   rueck = rueck.Trim(" {}[]");
   rueck = rueck.Replace('\n','');
   rueck = rueck.Replace('\r','');
   rueck = rueck.Replace('_','-');
   rueck = rueck.Replace(TrennerArray,TrennerArrayErsatz);
   if (rueck.Substr(0,1) == "[" ) { rueck = "data:"#rueck; }
   WriteLine("=== nach Replace === '"#rueck#"'");


! Array und Payload aufbereiten
   integer pos1 = 0; integer posG = 0;
   integer pos2 = rueck.Length();
   string temp = ""; string temp2;
   string teil;
   boolean Array = false; boolean Payload = false;
   while ( pos1 < pos2 )
	 {
         teil = rueck.Substr(pos1,1);
         if ( teil == "[")
            {! Array gefunden
            Array = true;
            }
         if ( (teil == "," ) && Array )
           {! Komma gegen Schraegstrich tauschen
            teil = TrennerArray;
            }
         if ( teil == "]")
            {! Array Ende
            Array = false;
            }
         if (  teil == '"' ) {
            if ( Payload )
            {! Payload Ende
            Payload = false;
            } else {
            ! Payload Start
            Payload = true;
            }
            }
         if ( ( ersetzen.Contains(teil) )  && Payload )
           {! verbotene Zeichen austauschen
            teil = durch;
            }
         temp = temp#teil;
         pos1 = pos1 + 1;
		 }
   rueck = temp;
   rueck = rueck.Replace('"','');
   WriteLine("=== nach Aufbereitung === '"#rueck#"'");
   WriteLine("=== Output V1.11 ===");

   string Part; string SVNameArray;
   string Gruppe = ""; string GruppeNeu;
   integer G1; integer G2;
   integer ArrayZaehler = 0;
   boolean ArrayMitGruppe = false;

   foreach (Part, rueck.Split(",") ){
          if (Part.Contains(":"))
          {
           if (Part.Contains("{"))
           { ! Unter-Objekt gefunden
             !WriteLine("Z96 Part='"#Part#"'"); ! *** DEBUG

             if (Part.Contains("[")) {
               ! enthält Arrray
               GruppeNeu = Part.StrValueByIndex(":", 0);
               GruppeNeu = GruppeNeu.Trim() #"_";
               Part = Part.Substr(GruppeNeu.Length()+2).Trim("{ ");
               Gruppe = Gruppe # GruppeNeu; Gruppe = Gruppe.Replace("__","_");
               !WriteLine("Gruppe alt:"#Gruppe); ! DEBUG ***
               !WriteLine("Inhalt alt:"#Part); ! DEBUG ***
             } else {
               ! enthält kein Array, aber möglicherweise geschachtelte Objekte
               temp = Part.Replace("{",";");
               G2 = web.webGetValueListCount(temp);
               G1=0;
               while (G1 < (G2-1)) {
               GruppeNeu = web.webGetValueFromList(temp,(G1)).Trim(" :");
               if ( (GruppeNeu == "_") || (GruppeNeu == "") ) {
               	    ArrayZaehler = ArrayZaehler + 1;
                    GruppeNeu = ArrayZaehler.ToString(0).Trim() # "_";
                    }
               Gruppe = Gruppe # GruppeNeu # "_";
               G1 = G1 + 1;
               }
               if ( Gruppe == "_" ) {
               	    ArrayZaehler = ArrayZaehler + 1;
                    Gruppe = ArrayZaehler.ToString(0).Trim() # "_";
                    }
               Gruppe = Gruppe.Replace("__","_");
               Part = web.webGetValueFromList(temp,(G2-1));
               !WriteLine("Gruppe neu:"#Gruppe); ! *** DEBUG
               !WriteLine("Inhalt neu:"#Part); ! **** DEBUG
             }

             !WriteLine("=== GRUPPE-START: "#Gruppe);
           }
           if (Part.Contains(TrennerArray)) {
              ! Array gefunden
              !WriteLine("=== ARRAY auflösen:"#Part);
              ArrayMitGruppe = false;
              Part = Part.Replace("[","");
              Part = Part.Replace("]","");
              pos1 = 0;  posG = 0;
              SVName = Gruppe;
              if (Part.Contains(":"))
                  {
                  SVName = Part.StrValueByIndex(":", 0);
                  Part = Part.Substr(SVName.Length()+1).Trim();
                  SVName = SVName.Trim("[{ ");
		  		  !WriteLine("*** Z365 ARRAY aufgelöst in:"#SVName#"==="#Part#"===");  ! DEBUG ***
                  }
              foreach (temp,Part.Split(TrennerArray))
              	{
                !WriteLine("Z352 temp="#temp); ! DEBUG ***

                if ( temp.Contains(":")) {
                ! Array enthält eine Gruppe

                  SVName = temp.StrValueByIndex(":", 0).Trim("{ ");
                  temp2 = temp;
                  temp = temp.StrValueByIndex(":", 1).Trim(" []");
                  SVNameArray = SVName#posG.ToString(0).Trim();
                      if ( temp.Contains("{")) {
                      ! weitere Gruppe gefunden
                      ArrayMitGruppe = true;
                      Gruppe = Gruppe # SVName # "_";
                      temp = temp.Trim(" {"); ! temp.StrValueByIndex("{", 1).Trim();
                      SVName = temp;
                      temp = temp2.StrValueByIndex(":", 2).Trim(" []");
                      SVNameArray = SVName#posG.ToString(0).Trim();
                      !WriteLine("Z363 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
                      }

                  if ( temp.Contains("}")) { posG = posG + 1; }
                  !WriteLine("Z367 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
                  SVName_kpl = prefix # Gruppe # SVNameArray;
                  WriteLine (SVName_kpl #" = '"# temp.Trim(" {[]}") #"'");
		          ! pruefen ob der SV NAme ersetzt werden muss
        		  if ( Name_original.Contains(SVName_kpl))
           			 {
                	 i = 0;
                	 foreach(itemID, Name_original.Split(";"))
						{
						WriteLine(i#": "#itemID);
                        if ( itemID == SVName_kpl )
                        	{
                            SVName_kpl = web.webGetValueFromList(Name_neu, i);
                            break;
                            }
						i = i + 1;
						}
                	 }
	              SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get(SVName_kpl);
     	          if (SV) {
                   SV.State(temp.Trim(" {[]}"));
                   WriteLine("=== SV "#SVName_kpl #" written (1) ===");
                   }
                } else {
                ! keine Gruppe
                  SVNameArray = SVName#pos1.ToString(0).Trim();
                  SVName_kpl = prefix # Gruppe # SVNameArray;
                  WriteLine (SVName_kpl #" = '"# temp.Trim(" {[]}") #"'");
		          ! pruefen ob der SV NAme ersetzt werden muss
        		  if ( Name_original.Contains(SVName_kpl))
           			 {
                	 i = 0;
                	 foreach(itemID, Name_original.Split(";"))
						{
						WriteLine(i#": "#itemID);
                        if ( itemID == SVName_kpl )
                        	{
                            SVName_kpl = web.webGetValueFromList(Name_neu, i);
                            break;
                            }
						i = i + 1;
						}
                	 }
	              SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get(SVName_kpl);
     	          if (SV) {
                   SV.State(temp.Trim(" {[]}"));
                   WriteLine("=== SV "#SVName_kpl #" written (2) ===");
                   }
                  pos1 = pos1 + 1;
                  }
           		if ( (temp.Contains("}")) && (ArrayMitGruppe)  )
           		{ ! Unter-Objekt zu Ende
               		  ! Gruppe um letzten Eintrag kürzen
               		  temp2 = Gruppe.Replace("_",";");
               		  G2 = web.webGetValueListCount(temp2);
                      !WriteLine("Z365 GRUPPE: "#Gruppe#" ==="); ! DEBUG ***
               		  Gruppe = ""; G1=0;
               				while (G1 < (G2-2)) {
               				Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
                			!WriteLine("Z368 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
               				G1 = G1 + 1;
               				}
             		!WriteLine("=== Z374 GRUPPE-ENDE: "#Gruppe#" ===");
                    ArrayMitGruppe = false;
           		}


                }
                if ( posG > 0 ) {
                	!WriteLine("Z413 Part="#Part); ! *** DEBUG

              		! Gruppe um letzten Eintrag kürzen
               		temp2 = Gruppe.Replace("_",";");
               		G2 = web.webGetValueListCount(temp2);
                    !WriteLine("=== GRUPPE-ENDE (array): "#Gruppe#"***");
               		Gruppe = ""; G1=0;
               		while (G1 < (G2-2)) {
               				Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
                			!WriteLine("Z422 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
               				G1 = G1 + 1;
               				}

                        }
			! ARRAY ENDE


           } else {
           ! ohne Array
           ! WriteLine("Z154 Part='"#Part#"'"); ! *** DEBUG
           Part = Part.Replace(":",";");
           SVName = Gruppe#web.webGetValueFromList(Part,0).Trim();
           Wert = web.webGetValueFromList(Part,1).RTrim("}").Trim(" {[]}");
           SVName_kpl = prefix # SVName;
           WriteLine (SVName_kpl #" = '"# Wert #"'");
           ! pruefen ob der SV NAme ersetzt werden muss
           if ( Name_original.Contains(SVName_kpl))
           		{
                i = 0;
                foreach(itemID, Name_original.Split(";"))
						{
						WriteLine(i#": "#itemID);
                        if ( itemID == SVName_kpl )
                        	{
                            SVName_kpl = web.webGetValueFromList(Name_neu, i);
                            break;
                            }
						i = i + 1;
						}
                }
	       SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get(SVName_kpl);
     	   if (SV) {
                   SV.State(Wert);
                   WriteLine("=== SV "#SVName_kpl #" written (3) ===");
                   }
           if (Part.Contains("}"))
             { ! Unter-Objekt zu Ende
             ! *** Gruppe muss um so viele Einträge gekürzt werden, wie } vorhanden sind in Part ***
             Part = Part.Replace(" ","");
             ! WriteLine("Z199 Part='"#Part#"'"); ! *** DEBUG
             while ( Part.EndsWith("}") ) {
               ! Gruppe um letzten Eintrag kürzen
               temp = Gruppe.Replace("_",";");
               G2 = web.webGetValueListCount(temp);
               Gruppe = ""; G1=0;
               while (G1 < (G2-2)) {
               		Gruppe = Gruppe # web.webGetValueFromList(temp,(G1)) # "_";
               		! WriteLine("=== "#G1#":"#Gruppe); *** DEBUG
               		G1 = G1 + 1;
               		}
               Part = Part.Substr(0,(Part.Length()-1)) ;
               ! WriteLine("Z212 Part="#Part); ! *** DEBUG
             } ! Ende while
             !WriteLine("=== GRUPPE-ENDE: "#Gruppe#" ===");
            }
           }
         }
   }
} else {
  WriteLine("Error at HTTP Request");
}
ältere Version V1.11:

Code: Alles auswählen

!JSON universell auslesen
!V1.11 MichaelN https://homematic-forum.de/forum/viewtopic.php?f=18&t=68762
!GPL-3.0-or-later
string url = "http://IP/";
url = "wget --no-check-certificate --timeout=2 -O - '"#url#"'";
string prefix = "JSON_";
string CUXD_Device = ""; ! falls man CUxD  nutzt, Device eintragen z.B. "CUxD.CUX2801001:1"
! optional kann man Texte am Anfang und Ende abschneiden
string Start_JSON =''; ! 'Hier den Start des eigentlichen JSON-Codes eingeben'
string Ende_JSON =''; ! 'Hier den Ende des eigentlichen JSON-Codes eingeben'
! Trenner kann notfalls angepasst werden, wenn es mit den Nutzdaten kollidiert
string TrennerArray = " \ ";  TrennerArray = TrennerArray.Trim();
string TrennerArrayErsatz = "/";
! festlegen welche Zeichen in den Nutzdaten durch welches Zeichen ersetzt werden
string ersetzen = ":,[]{}";
string durch = " ";

string rueck;
string error;
object SV;
string SVName;
var Wert;

!Testdaten rauslöschen für produktiven Betrieb
rueck = '{
  "Vorname": "Ronald",
  "Nachname": "Reagan",
  "Geburtsdatum": "1911-02-06",
  "Nationalitaet": "US-amerikanisch",
  "Partei":
  {
    "Name": "Republican Party",
    "Synonyme": ["Republikaner","Grand Old Party", "GOP"],
    "Hauptsitz": "Washington/D.C.",
    "Gründungsdatum": "1854-03-20",
    "Gründungsort": "Ripon"
  },
  "Amt": "US-Präsident",
  "Hobbys": ["Reiten", "Golfen", "Lesen"],
  "verheiratet": true,
  "Kinder": [
    {
      "Vorname": "Michael",
      "Nachname": "Reagan"    },
   {
      "Vorname": "Patti",
      "Nachname": "Davis"    },
   {
      "Vorname": "Ron",
      "Nachname": "Reagan"    }]
}
';
! Ende Testdaten


!- Abruf der Daten
if ( !CUXD_Device ) {
! system.exec
system.Exec(url , &rueck, &error);
} else {
! CUxD
dom.GetObject(CUXD_Device#".CMD_SETS").State(url);
dom.GetObject(CUXD_Device#".CMD_QUERY_RET").State(1);
rueck = dom.GetObject(CUXD_Device#".CMD_RETS").State();
}



if (!rueck== "")
{
   WriteLine("=== HTTP-Request OK === "#rueck);
   if ( Start_JSON.Length() > 0 ) {
       ! JSON am Anfang kürzen
	   rueck = rueck.Substr(rueck.Find(Start_JSON));
   }
   if ( Ende_JSON.Length() > 0 ) {
       ! JSON am Ende kürzen
	   rueck = rueck.Substr(0,rueck.Find(Ende_JSON));
   }
   rueck = rueck.ToLatin(); ! wegen deutschen Umlauten
   rueck = rueck.Trim(" {}[]");
   rueck = rueck.Replace('\n','');
   rueck = rueck.Replace('\r','');
   rueck = rueck.Replace('_','-');
   rueck = rueck.Replace(TrennerArray,TrennerArrayErsatz);
   if (rueck.Substr(0,1) == "[" ) { rueck = "data:"#rueck; }
   WriteLine("=== nach Replace === '"#rueck#"'");


! Array und Payload aufbereiten
   integer pos1 = 0; integer posG = 0;
   integer pos2 = rueck.Length();
   string temp = ""; string temp2;
   string teil;
   boolean Array = false; boolean Payload = false;
   while ( pos1 < pos2 )
	 {
         teil = rueck.Substr(pos1,1);
         if ( teil == "[")
            {! Array gefunden
            Array = true;
            }
         if ( (teil == "," ) && Array )
           {! Komma gegen Schraegstrich tauschen
            teil = TrennerArray;
            }
         if ( teil == "]")
            {! Array Ende
            Array = false;
            }
         if (  teil == '"' ) {
            if ( Payload )
            {! Payload Ende
            Payload = false;
            } else {
            ! Payload Start
            Payload = true;
            }
            }
         if ( ( ersetzen.Contains(teil) )  && Payload )
           {! verbotene Zeichen austauschen
            teil = durch;
            }
         temp = temp#teil;
         pos1 = pos1 + 1;
		 }
   rueck = temp;
   rueck = rueck.Replace('"','');
   WriteLine("=== nach Aufbereitung === '"#rueck#"'");
   WriteLine("=== Output V1.11 ===");

   string Part; string SVNameArray;
   string Gruppe = ""; string GruppeNeu;
   integer G1; integer G2;
   integer ArrayZaehler = 0;
   boolean ArrayMitGruppe = false;

   foreach (Part, rueck.Split(",") ){
          if (Part.Contains(":"))
          {
           if (Part.Contains("{"))
           { ! Unter-Objekt gefunden
             !WriteLine("Z96 Part='"#Part#"'"); ! *** DEBUG

             if (Part.Contains("[")) {
               ! enthält Arrray
               GruppeNeu = Part.StrValueByIndex(":", 0);
               GruppeNeu = GruppeNeu.Trim() #"_";
               Part = Part.Substr(GruppeNeu.Length()+2).Trim("{ ");
               Gruppe = Gruppe # GruppeNeu; Gruppe = Gruppe.Replace("__","_");
               !WriteLine("Gruppe alt:"#Gruppe); ! DEBUG ***
               !WriteLine("Inhalt alt:"#Part); ! DEBUG ***
             } else {
               ! enthält kein Array, aber möglicherweise geschachtelte Objekte
               temp = Part.Replace("{",";");
               G2 = web.webGetValueListCount(temp);
               G1=0;
               while (G1 < (G2-1)) {
               GruppeNeu = web.webGetValueFromList(temp,(G1)).Trim(" :");
               if ( (GruppeNeu == "_") || (GruppeNeu == "") ) {
               	    ArrayZaehler = ArrayZaehler + 1;
                    GruppeNeu = ArrayZaehler.ToString(0).Trim() # "_";
                    }
               Gruppe = Gruppe # GruppeNeu # "_";
               G1 = G1 + 1;
               }
               if ( Gruppe == "_" ) {
               	    ArrayZaehler = ArrayZaehler + 1;
                    Gruppe = ArrayZaehler.ToString(0).Trim() # "_";
                    }
               Gruppe = Gruppe.Replace("__","_");
               Part = web.webGetValueFromList(temp,(G2-1));
               !WriteLine("Gruppe neu:"#Gruppe); ! *** DEBUG
               !WriteLine("Inhalt neu:"#Part); ! **** DEBUG
             }

             !WriteLine("=== GRUPPE-START: "#Gruppe);
           }
           if (Part.Contains(TrennerArray)) {
              ! Array gefunden
              !WriteLine("=== ARRAY auflösen:"#Part);
              ArrayMitGruppe = false;
              Part = Part.Replace("[","");
              Part = Part.Replace("]","");
              pos1 = 0;  posG = 0;
              SVName = Gruppe;
              if (Part.Contains(":"))
                  {
                  SVName = Part.StrValueByIndex(":", 0);
                  Part = Part.Substr(SVName.Length()+1).Trim();
                  SVName = SVName.Trim("[{ ");
		  !WriteLine("*** Z365 ARRAY aufgelöst in:"#SVName#"==="#Part#"===");  ! DEBUG ***
                  }
              foreach (temp,Part.Split(TrennerArray))
              	{
                !WriteLine("Z352 temp="#temp); ! DEBUG ***

                if ( temp.Contains(":")) {
                ! Array enthält eine Gruppe

                  SVName = temp.StrValueByIndex(":", 0).Trim("{ ");
                  temp2 = temp;
                  temp = temp.StrValueByIndex(":", 1).Trim(" []");
                  SVNameArray = SVName#posG.ToString(0).Trim();
                      if ( temp.Contains("{")) {
                      ! weitere Gruppe gefunden
                      ArrayMitGruppe = true;
                      Gruppe = Gruppe # SVName # "_";
                      temp = temp.Trim(" {"); ! temp.StrValueByIndex("{", 1).Trim();
                      SVName = temp;
                      temp = temp2.StrValueByIndex(":", 2).Trim(" []");
                      SVNameArray = SVName#posG.ToString(0).Trim();
                      !WriteLine("Z363 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
                      }

                  if ( temp.Contains("}")) { posG = posG + 1; }
                  !WriteLine("Z367 Gruppe="#Gruppe#" SVNameAray="#SVNameArray#" temp="#temp); ! DEBUG ***
                  WriteLine (prefix # Gruppe # SVNameArray #" = '"# temp.Trim(" {[]}") #"'");
	              SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # Gruppe # SVNameArray));
     	          if (SV) {
                   SV.State(temp.Trim(" {[]}"));
                   WriteLine("=== SV "#prefix # Gruppe # SVNameArray #" written ===");
                   }
                } else {
                ! keine Gruppe
                  SVNameArray = SVName#pos1.ToString(0).Trim();
                  WriteLine (prefix # Gruppe # SVNameArray #" = '"# temp.Trim(" {[]}") #"'");
	              SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # Gruppe # SVNameArray));
     	          if (SV) {
                   SV.State(temp.Trim(" {[]}"));
                   WriteLine("=== SV "#prefix # Gruppe # SVNameArray #" written ===");
                   }
                  pos1 = pos1 + 1;
                  }
           		if ( (temp.Contains("}")) && (ArrayMitGruppe)  )
           		{ ! Unter-Objekt zu Ende
               		  ! Gruppe um letzten Eintrag kürzen
               		  temp2 = Gruppe.Replace("_",";");
               		  G2 = web.webGetValueListCount(temp2);
                      !WriteLine("Z365 GRUPPE: "#Gruppe#" ==="); ! DEBUG ***
               		  Gruppe = ""; G1=0;
               				while (G1 < (G2-2)) {
               				Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
                			!WriteLine("Z368 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
               				G1 = G1 + 1;
               				}
             		!WriteLine("=== Z374 GRUPPE-ENDE: "#Gruppe#" ===");
                    ArrayMitGruppe = false;
           		}


                }
                if ( posG > 0 ) {
                	!WriteLine("Z413 Part="#Part); ! *** DEBUG

              		! Gruppe um letzten Eintrag kürzen
               		temp2 = Gruppe.Replace("_",";");
               		G2 = web.webGetValueListCount(temp2);
                    !WriteLine("=== GRUPPE-ENDE (array): "#Gruppe#"***");
               		Gruppe = ""; G1=0;
               		while (G1 < (G2-2)) {
               				Gruppe = Gruppe # web.webGetValueFromList(temp2,(G1)) # "_";
                			!WriteLine("Z422 "#G1#"/"#G2#":"#Gruppe); ! *** DEBUG
               				G1 = G1 + 1;
               				}

                        }
			! ARRAY ENDE


           } else {
           ! ohne Array
           ! WriteLine("Z154 Part='"#Part#"'"); ! *** DEBUG
           Part = Part.Replace(":",";");
           SVName = Gruppe#web.webGetValueFromList(Part,0).Trim();
           Wert = web.webGetValueFromList(Part,1).RTrim("}").Trim(" {[]}");
           WriteLine (prefix # SVName #" = '"# Wert #"'");
	       SV = dom.GetObject(ID_SYSTEM_VARIABLES).Get((prefix # SVName));
     	   if (SV) {
                   SV.State(Wert);
                   WriteLine("=== SV "#prefix # SVName #" written ===");
                   }
           if (Part.Contains("}"))
             { ! Unter-Objekt zu Ende
             ! *** Gruppe muss um so viele Einträge gekürzt werden, wie } vorhanden sind in Part ***
             Part = Part.Replace(" ","");
             ! WriteLine("Z199 Part='"#Part#"'"); ! *** DEBUG
             while ( Part.EndsWith("}") ) {
               ! Gruppe um letzten Eintrag kürzen
               temp = Gruppe.Replace("_",";");
               G2 = web.webGetValueListCount(temp);
               Gruppe = ""; G1=0;
               while (G1 < (G2-2)) {
               		Gruppe = Gruppe # web.webGetValueFromList(temp,(G1)) # "_";
               		! WriteLine("=== "#G1#":"#Gruppe); *** DEBUG
               		G1 = G1 + 1;
               		}
               Part = Part.Substr(0,(Part.Length()-1)) ;
               ! WriteLine("Z212 Part="#Part); ! *** DEBUG
             } ! Ende while
             !WriteLine("=== GRUPPE-ENDE: "#Gruppe#" ===");
            }
           }
         }
   }
} else {
  WriteLine("Error at HTTP Request");
}
Beispiel:
Aus diesen Testdaten (Quelle Wikipedia)

Code: Alles auswählen

{
  "Vorname": "Ronald",
  "Nachname": "Reagan",
  "Geburtsdatum": "1911-02-06",
  "Nationalitaet": "US-amerikanisch",
  "Partei":
  {
    "Name": "Republican Party",
    "Synonyme": ["Republikaner", "Grand Old Party", "GOP"],
    "Hauptsitz": "Washington/D.C.",
    "Gründungsdatum": "1854-03-20",
    "Gründungsort": "Ripon"
  },
  "Amt": "US-Präsident",
  "Hobbys": ["Reiten", "Golfen", "Lesen"],
  "verheiratet": true,
  "Kinder": [
    {
      "Vorname": "Michael",
      "Nachname": "Reagan"    },
   {
      "Vorname": "Patti",
      "Nachname": "Davis"    },
   {
      "Vorname": "Ron",
      "Nachname": "Reagon"    }]
}
wird folgende Ausgabe generiert:

Code: Alles auswählen

nach Replace: '  Vorname: Ronald,  Nachname: Reagan,  Geburtsdatum: 1911-02-06,  Nationalitaet: US-amerikanisch,  Partei:  {    Name: Republican Party,    Synonyme: [Republikaner, Grand Old Party, GOP],    Hauptsitz: Washington/D.C.,    Gründungsdatum: 1854-03-20,    Gründungsort: Ripon  },  Amt: US-Präsident,  Hobbys: [Reiten, Golfen, Lesen],  verheiratet: true,  Kinder: [    {      Vorname: Michael,      Nachname: Reagan    },   {      Vorname: Patti,      Nachname: Davis    },   {      Vorname: Ron,      Nachname: Reagon    }]}'
nach Array-Aufbereitung: '  Vorname: Ronald,  Nachname: Reagan,  Geburtsdatum: 1911-02-06,  Nationalitaet: US-amerikanisch,  Partei:  {    Name: Republican Party,    Synonyme: [Republikaner\ Grand Old Party\ GOP],    Hauptsitz: Washington/D.C.,    Gründungsdatum: 1854-03-20,    Gründungsort: Ripon  },  Amt: US-Präsident,  Hobbys: [Reiten\ Golfen\ Lesen],  verheiratet: true,  Kinder: [    {      Vorname: Michael\      Nachname: Reagan    }\   {      Vorname: Patti\      Nachname: Davis    }\   {      Vorname: Ron\      Nachname: Reagon    }]}'
=== Output ===
JSON_Vorname = 'Ronald'
JSON_Nachname = 'Reagan'
JSON_Geburtsdatum = '1911-02-06'
JSON_Nationalitaet = 'US-amerikanisch'
=== GRUPPE-START: Partei_
JSON_Partei_Name = 'Republican Party'
=== ARRAY auflösen:    Synonyme: [Republikaner\ Grand Old Party\ GOP]
JSON_Partei_Synonyme0 = 'Republikaner'
JSON_Partei_Synonyme1 = 'Grand Old Party'
JSON_Partei_Synonyme2 = 'GOP'
JSON_Partei_Hauptsitz = 'Washington/D.C.'
JSON_Partei_Gründungsdatum = '1854-03-20'
JSON_Partei_Gründungsort = 'Ripon'
=== GRUPPE-ENDE: Partei_***
JSON_Amt = 'US-Präsident'
=== ARRAY auflösen:  Hobbys: [Reiten\ Golfen\ Lesen]
JSON_Hobbys0 = 'Reiten'
JSON_Hobbys1 = 'Golfen'
JSON_Hobbys2 = 'Lesen'
JSON_verheiratet = 'true'
=== GRUPPE-START: Kinder_
=== ARRAY auflösen:[    {      Vorname: Michael\      Nachname: Reagan    }\   {      Vorname: Patti\      Nachname: Davis    }\   {      Vorname: Ron\      Nachname: Reagon    }]}
JSON_Kinder_Vorname0 = 'Michael'
JSON_Kinder_Nachname0 = 'Reagan'
JSON_Kinder_Vorname1 = 'Patti'
JSON_Kinder_Nachname1 = 'Davis'
JSON_Kinder_Vorname2 = 'Ron'
JSON_Kinder_Nachname2 = 'Reagon'
=== GRUPPE-ENDE: Kinder_***
Will man den Vornamen in eine Systemvariable schreiben lassen, dann legt man eine SV JSON_Vorname an.
Braucht man den zweiten Wert des Array Hobbys, dann legt man eine SV JSON_Hobbys1 an. Achtung: Arrays werde bei 0 startend nummeriert.
Benötigt man den Wert Name aus dem Partei-Objekt, dann lautet der SV-Name JSON_Partei_Name.

Re: JSON-APIs auswerten

Verfasst: 03.08.2021, 19:04
von MichaelN
Version 1.5 veröffentlicht. Nun sind auch Gruppen in Arrays möglich.
Wenn jemand auf ein JSON stößt, das sich nicht auswerten lässt (sehr komplexe stoßen immer noch an Grenzen) kann es hier ja mal in Code Tags posten. Dann kann ich es mir ansehen.

Re: JSON-APIs auswerten

Verfasst: 03.08.2021, 19:58
von Black
ich hab grad mal probeweise dein Script mit Backup ausgaben des SDV gequält, programmBackup und complete-Device-Backup.
es lief durch und die Ausgaben waren auf den erstblick erstmal stimmig.

gute Arbeit

Black

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 14:26
von MichaelN
Dummerweise führt

Code: Alles auswählen

rueck = rueck.Replace(^\^,'/');
Und die sonstigen Vorkommen von ^ zur nicht Nutzbarkeit Speicherbarkeit des Skript auf einer Standard CCU

Meine schlaue Idee war eigentlich das \ als Trennzeichen zu nutzen, da es eher nicht in einem JSON vorkommen sollte.
Leider schlägt dann der Quoting-Teufel zu, dem ich bisher elegant ausgewichen bin.

Lt. Doku müsste ich "\\" schreiben um das \ zu Quoten. Das geht ja bekanntlich nciht. "\\\" führt auch zum RunTimeError.
Wie bekomme ich denn nun \ in einen String, ohne ^ zu benutzen? Und wäre das dann überhaupt CCU und RM kompatibel?

Ich glaube, ich weiche auf einen anderen Trenner aus. * z. B. Sollte auch nicht so häufig in Messdaten o. Ä. Vorkommen?!

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 14:41
von Tyfys
Also du willst den Backslash in einen Schrägstrich wandeln.

und dir macht das Accent circonflexe ^ die Speicherung zu nichte

Das sollte doch so gehen :

Code: Alles auswählen

rueck = rueck.Replace("\",'/');
Oder hab ich das Problem nicht erkannt ?

Gruß
Harry

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 14:45
von MichaelN
Sollte oder hast Du es ausprobiert?

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 15:09
von Tyfys
leider hab ich das nicht ausprobiert, weil ich dachte es geht nur um ^.

Ein Skript mit diesem Zeichen ^ wird auf meiner CCU2 nicht gespeichert.

Nun hab ich meine Idee ausprobiert.
Der Backslash wirft dann bei der Fehlerprüfung einen Fehler aus.
Im Skrip testen tut sich auch nix.

ist ja tricky


Gruß
Harry

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 18:06
von Tyfys
Hallo Michael

Hab auch mal ein wenig rum experimentiert.
Einige Commands haben mit dem "\" keine Probleme, wenn solo definiert ist.


Mit Trick 17 kann man dann aus einem \ ein / machen
bzw. auch aus / ein \

Code: Alles auswählen

string x="Hallo\die\Welt!";
WriteLine(x);
string bs=" \ ";
WriteLine(bs.Trim());
x=x.Replace(bs.Trim(),"/");
WriteLine(x);
x=x.Replace("/",bs.Trim());
WriteLine(x);

x="Hallo\die\Welt!";
x=x.Replace(" \ ".Trim(),"/");
WriteLine(x);

Code: Alles auswählen

Hallo\die\Welt!
\
Hallo/die/Welt!
Hallo\die\Welt!
Hallo/die/Welt!

Abspeichern lässt sich das auch.

Vielleicht hilft es dir weiter, wenn du zu dem selben Ergebnis kommst.

Gruß
Harry

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 18:27
von Black
Das escaping, gerade wenn es compatible mit einer ccu3 / RMatik sein soll, kann echt eine spassige Angelegenheit sein.
Da kann ich auch ein Liedchen von singen.

sobald das ^ benutzt wird, kriegst du ohne umweg das script nicht ins programm. mit der WebUI gehts dann nicht mehr.

und wie du gemerkt hast \\ bzw \\\ tuts nicht so wie erwartet und wenn in dem einen system funktioniert dann im anderen nicht mehr.

black

Re: JSON-APIs auswerten

Verfasst: 04.08.2021, 18:38
von jmaus
Tyfys hat geschrieben:
04.08.2021, 14:41
Das sollte doch so gehen :

Code: Alles auswählen

rueck = rueck.Replace("\",'/');
Das wird zwar auf einer CCU3 bzw. CCU2 mit aktueller Firmware (3.59.x/2.59x) noch gehen, aber syntaktisch ist das falsch und in Zukunft wird es dazu Updates geben, die eine neuere ReGaHss version mit sich bringen werden die das dann reparieren und es dann notwendig machen das man das korrekterweise wie folgt schreibt:

Code: Alles auswählen

rueck = rueck.Replace("\\",'/');
Denn ein einzelnes Backslash (\) ist genau dafür da (wie man das erwarten sollte) das darauf folgende Zeichen zu escapen. Insofern ist das "\" eben falsch weil das Backslash das darauf folgende " zeichen escapt und dann erwartet das danach noch ein " zum schließen der zeichenkette folgt oder aber es sind eben zwei aufeinanderfolgende Backslash (\\) die dann eben unterm Strich ein einzelnes im String auftauchen lassen.

Wenn man das als kompatibel zu kommenden Versionen bzw. zu RaspberryMatic (weil das schon die aktuellste ReGaHss einsetzt) halten will, dann muss man hier in der Tat folgenden Aufruf verwenden:

Code: Alles auswählen

rueck = rueck.Replace(^\^,'/');
Und eben folglich den "super-char" (^) verwenden der normalerweise innerhalb seines content (d.h. also bis zum nächsten ^) keinerlei escaping zulässt und somit ein einzelnes \ darin i.O. ist. Das Problem hierbei ist jedoch, das durch einen anderen Bug in der WebUI selbst (d.h. im Programm-Editor) es mitunter nicht möglich ist den super-char (^) im WebUI Editor für Programme zu verwenden. Auch das ist in RaspberryMatic bereits repariert, aber eben noch nicht in die CCU3 Firmware zurückgeflossen.