FHEM Forum

FHEM => Automatisierung => Thema gestartet von: KyleK am 18 September 2022, 19:32:29

Titel: [JsonMod] Frage zu JSONPath Features
Beitrag von: KyleK am 18 September 2022, 19:32:29
Hallo,

ich hab noch eine Verständnisfrage, damit ich nicht in der falschen Richtung weiterprobiere.
Ich wollte folgenden jsonPath nutzen zum Filtern von Elementen:

jsonPath('$.orderDays[?(@.date == $.to)].orderedMeals[?(@.quantity==1)]')

Bei Goessner und Jayway funktioniert das, aber hier leider nicht.
Ich vermute mal:

Leicht reduziertes JSON-Beispiel:

{
    "from": "2022-09-18",
    "to": "2022-09-19",
    "orderDays": [
        {
            "date": "2022-09-19",
            "orderedMeals": [
                {
                    "meal": {
                        "name": "Cremige Karotten-Kokossuppe"
                    },
                    "quantity": 0
                },
                {
                    "meal": {
                        "name": "H\u00fchnerfrikassee, "
                    },
                    "quantity": 1
                },
                {
                    "meal": {
                        "name": "Lachsw\u00fcrfel in Sahneso\u00dfe,"
                    },
                    "quantity": 0
                }
            ]
        }
    ]
}
Titel: Antw:[JsonMod] Frage zu JSONPath Features
Beitrag von: yersinia am 06 Oktober 2022, 12:52:18
Ich glaube nicht, dass du dieses JSON vernünftig mit JsonMod derart ausgelesen bekommst, dass du nur das gewählte Menü für einen bestimmten Tag raus gefiltert bekommst. Du kannst zwar die Tage herausfinden, wann ein Menü bestellt worden ist
multi(jsonPath("\$.orderDays[?(\@.orderedMeals..quantity in ['1'])]"), 'date', property('.date'));
oder das bestellte Menü rausselektieren
multi(jsonPath("\$.orderDays..orderedMeals[?(\@.quantity in ['1'])]"), 'meal', property('.meal.name'));
aber die dann erzeugten readings stehen nicht zwangsweise in einem logischen Zusammenhang. Aufgrund der gelieferten Json-Struktur kann JsonMod imho hier keine Kombination auslesen - und logische Verknüpfungen wird es (erstmal) nicht geben (https://forum.fhem.de/index.php/topic,111489.msg1234534.html#msg1234534).

Wie betateilchen schreibt (https://forum.fhem.de/index.php/topic,111489.msg1234528.html#msg1234528), würde ich das in eine Perl-Funktion auslagern.

Anfangen kannst du mit
complete();
und iterierst dann über die Readings
   READINGS:
     2022-10-06 12:00:56   .computedReadings orderDays.0.orderedMeals.1.quantity,orderDays.0.orderedMeals.0.quantity,orderDays.0.orderedMeals.2.meal.name,orderDays.0.orderedMeals.2.quantity,to,orderDays.0.orderedMeals.0.meal.name,orderDays.0.date,orderDays.0.orderedMeals.1.meal.name,from
     2022-10-06 12:00:56   from            2022-09-18
     2022-10-06 12:00:56   orderDays.0.date 2022-09-19
     2022-10-06 12:00:56   orderDays.0.orderedMeals.0.meal.name Cremige Karotten-Kokossuppe
     2022-10-06 12:00:56   orderDays.0.orderedMeals.0.quantity 0
     2022-10-06 12:00:56   orderDays.0.orderedMeals.1.meal.name Hühnerfrikassee,
     2022-10-06 12:00:56   orderDays.0.orderedMeals.1.quantity 1
     2022-10-06 12:00:56   orderDays.0.orderedMeals.2.meal.name Lachswürfel in Sahnesoße,
     2022-10-06 12:00:56   orderDays.0.orderedMeals.2.quantity 0
     2022-10-06 12:00:56   to              2022-09-19

hinweg. Die Readings bekommst du aus dem Reading .computedReadings.

Anbei ein (stümperhafte) Vorschlag eines userReadings, geht sicher auch eleganter und über eine eigene Funktion in der myUtils - aber es ist erstmal ein Denkanstoß. Es werden alle Readings durchgesucht bis das gewünschte Datum zutrifft (erste Schleife durchsucht erste Array), danach wird nach einem quantity reading mit dem Wert 1 gesucht (zweite Schleife durchsucht das zweite Array); wenn gefunden wird aus der Wert des .meal.name readings ausgegeben.
selectedMeal {
  my $ret = "";
   if (ReadingsVal($name,".computedReadings","") eq "") {
    $ret = "error 1";
  } else {
  my $mydate = "2022-09-19";
my $readingprefix = "";
my @readings = split(',',ReadingsVal($name,".computedReadings",""));
foreach my $reading (@readings) {
if($reading eq "to") { next; }
if($reading eq "from") { next; }
if(ReadingsVal($name,$reading,"none") eq $mydate) {
$readingprefix = substr($reading, 0, -5); #.date has 5 chars
last;
}
}
foreach my $reading (@readings) {
if(substr($reading, 0, length($readingprefix)) ne $readingprefix) { next; }
if(substr($reading, -8) ne "quantity") { next; } #
if(ReadingsVal($name,$reading,"none") eq "1") {
$readingprefix = substr($reading, 0, -9); #.quantity has 9 chars
$ret = ReadingsVal($name,($readingprefix.".meal.name"),"error 3");
last;
}
}
  }
  return ($ret eq "")?"error 2":$ret;
}

(Der Code setzt voraus, dass die Struktur und die Elemente immer gleich bleiben, ist also genau auf dieses Beispiel zugeschnitten.)

Ergibt bei deinem Json und wenn man nach "2022-09-19" sucht
     2022-10-06 12:47:12   selectedMeal    Hühnerfrikassee,

Mit einer nicht ganz so reduzierten Json Quelle könnte man noch weiter testen. Das Datum müsste man dann ebenso noch dynamisch anpassen, eventuell liesst man das dann direkt aus einem Reading aus - oder übergibt es der Funktion, oder, oder, oder...
Titel: Antw:[JsonMod] Frage zu JSONPath Features
Beitrag von: KyleK am 10 Oktober 2022, 23:49:22
Ich hab das ganze inzwischen so gelöst:
Diese readings hole ich über readingList:

multi(jsonPath('$.orderDays[0].orderedMeals[?(@.quantity==1)]'), sprintf('meal_%s_%s', property('meal.date'), count()), sprintf('%s %s', property('meal.name'), property('meal.description')));
multi(jsonPath('$.orderDays[1].orderedMeals[?(@.quantity==1)]'), sprintf('meal_%s_%s', property('meal.date'), count()), sprintf('%s %s', property('meal.name'), property('meal.description')));
single(jsonPath('$.from'), 'today', 'n/a');
single(jsonPath('$.to'), 'tomorrow', 'n/a');


Und dann hab ich noch userReasings definiert:

meal_today {
ReadingsVal("$name",'meal_'.ReadingsVal('di.gourmetta.Date','today','').'_0', '')
},
meal_tomorrow {
ReadingsVal("$name",'meal_'.ReadingsVal('di.gourmetta.Date','tomorrow','').'_0', '')
}


Damit habe ich 2 Readings ,,heute" und ,,morgen", die ich irgendwo ausgeben kann.

Mit dem JSONPath aus meinem Post ganz oben wäre es einfacher, aber so geht es :)