Verschachtelte JSON Objekte dynamisch zerlegen

Viele Dienste im Internet , z.B. Der Deutsche Wetterdienst, stellen Daten als verschachtelte JSON Objekte bereit. Diese JSON Objekte wollen wir heute dynamisch zerlegen und einzelne Datenpunkte in ioBroker erzeugen. Mit Hilfe von JavaScript ist es möglich die Datenpunkte zu erstellen. Den Schwerpunkt lege ich hier auf die verschachtelten JSON Objekte mit einer dynamischen Generierung von Datenpunktnamen und deren Werten.

Für die Erstellung von JavaScript in ioBroker brauchen wir den installierten JavaScript Script Engine Adapter. Die Installation von Adaptern in ioBroker beschreibe ich in einem anderen Artikel.

Ein klassisch verschachteltes JSON Objekt könnte so aussehen:

{"Time":"2021-02-16T20:00:58","BME280":{"Temperature":13.1,"Humidity":78.4,"DewPoint":9.4,"Pressure":999.8},"PressureUnit":"hPa","TempUnit":"C"}

Dieses JSON Objekt wird in diesem Fall von Tasmota für einen angeschlossenen Temperatursensor von Bosch erzeugt und mittels MQTT übermittelt. Es gibt diverse Adapter, die diese JSON Zeichenkette schon richtig in einzelne Datenpunkte zerlegen. Allerdings funktioniert das bisher nur über MQTT, aber nicht mit Daten die über eine API oder ähnlich aus dem Internet abgerufen werden.

Ich werde euch dennoch die Variante der statischen bzw. manuellen Zerlegung zuerst demonstrieren, sowie die Vor- und Nachteile erläutern. Ein JSON Objekt besteht immer aus einem Key -> Value Paar. Bei dem oben gezeigten JSON Objekt ist „Time“ der Key (Schlüssel) und „2021-02-16T20:00:58″,“BME280“ der Value (Wert).

Wichtiger Hinweis
Wichtiger Hinweis
Ab Version 5.2.x des JavaScript Adapters kann das parsen mit JSON.Parse zu einem Fehler führen. Ich werde die Beispiele weiter unten im Artikel entsprechend anpassen, folglich wird in diesem Beispiel die Adapter Version 5.1.3 verwendet.

Verschachtelte JSON Objekte statisch zerlegen

Fangen wir an unser JavaScript zu schreiben:

//Verschachteltes JSON Objekt - statisch

// Datenpunkte (wenn nicht vorhanden) anlegen (einmalig)
createState("0_userdata.0.Test.BME280.Zeit", "", {name: 'Zeitstempel',  type: 'string'});
createState("0_userdata.0.Test.BME280.Temperatur", "", {name: 'Temperatur in C°',  type: 'number', unit:'°C'});
createState("0_userdata.0.Test.BME280.Luftfeuchtigkeit", "", {name: 'relative Luftfeuchtigkeit in %',  type: 'number', unit:'%'});
createState("0_userdata.0.Test.BME280.Taupunkt", "", {name: 'Taupunkt',  type: 'number', unit:'°C'});
createState("0_userdata.0.Test.BME280.Luftdruck", "", {name: 'Luftdruck',  type: 'number', unit:'hPa'});

// Variablen deklarieren
var json, Zeit, Temperatur, Luftfeuchtigkeit, Taupunkt, Luftdruck, obj;

json = '{"Time":"2021-02-16T20:00:58","BME280":{"Temperature":13.1,"Humidity":78.4,"DewPoint":9.4,"Pressure":999.8},"PressureUnit":"hPa","TempUnit":"C"}';

   //try ist wichtig damit der Adapter nicht abschmiert bei einem Fehler
   try {
        obj = JSON.parse(json);
        } catch (e) {
            console.error('Cannot parse:');
            //return;
        }

// Variablen - Zuweisung von Werten (Values) über Punktnotation der Schlüssel (Keys)
    Zeit                = obj.Time;
    Temperatur          = parseFloat(obj.BME280.Temperature);
    Luftfeuchtigkeit    = parseFloat(obj.BME280.Humidity);
    Taupunkt            = parseFloat(obj.BME280.DewPoint);
    Luftdruck           = parseFloat(obj.BME280.Pressure);

// Datenpunkte mit Werten füllen
setState('0_userdata.0.Test.BME280.Zeit', (Zeit));
setState('0_userdata.0.Test.BME280.Temperatur', (Temperatur));
setState('0_userdata.0.Test.BME280.Luftfeuchtigkeit', (Luftfeuchtigkeit));
setState('0_userdata.0.Test.BME280.Taupunkt', (Taupunkt));
setState('0_userdata.0.Test.BME280.Luftdruck', (Luftdruck));


Im Quellcode ist zu sehen, dass jeder einzelne Datenpunkt einzeln definiert werden muss. Der Datentyp, Name, Standardwert und die Einheit können individuell vergeben werden. Bei den im Beispiel aufgeführten Daten ist das auch gut zu bewerkstelligen, doch allerdings gibt es auch weit umfangreichere verschachtelte JSON Objekte.

In der Praxis würden wir den Quellcode ausführen lassen, wenn sich der Datenpunkt mit dem JSON Objekt aktualisiert. Der Trigger kann einfach in den vorhandenen Quellcode eingefügt werden.

Dynamische Zerlegung des JSON Objekts

Das oben aufgeführte Beispiel kann dynamisch zerlegt werden, wenn wir ein paar Zeilen Quellcode ändern.

//Verschachteltes JSON Object - dynamisch

var data = '{"Time":"2021-02-16T20:00:58","BME280":{"Temperature":13.1,"Humidity":78.4,"DewPoint":9.4,"Pressure":999.8},"PressureUnit":"hPa","TempUnit":"C"}';

var obj2 = JSON.parse(data);

for (var key in obj2) {
    console.log(key + ' => ' + obj2[key]);
    if (key === "Time") {
        createState("0_userdata.0.Test2.BME280." + key , "", {name: key,  type: 'string'});
        setState('0_userdata.0.Test2.BME280.' + key , obj2[key], true);
    }
}

for (var key in obj2.BME280) {
    console.log(key + ' => ' + obj2.BME280[key]);
    createState("0_userdata.0.Test2.BME280." + key , "", {name: key,  type: 'number'});
    setState('0_userdata.0.Test2.BME280.' + key , obj2.BME280[key], true);
}

Ihr könnt sehen, dass der Quellcode um einiges kürzer geworden ist. Mit dieser Variante hat man keinen Einfluss mehr auf die Namen der Datenpunkte. Der größte Vorteil hierbei: Bei sehr umfangreichen JSON Objekten muss wenig Aufwand betrieben werden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

* Bitte akzeptiere die Bestimmungen zum Datenschutz.