[JsonMod] Frage zu JSONPath Features

Begonnen von KyleK, 18 September 2022, 19:32:29

Vorheriges Thema - Nächstes Thema

KyleK

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:

  • 2 filter expressions in einem JsonPath werden nicht unterstützt?
  • Ein Vergleich "@.date == $.to" ist auch nicht möglich, weil $.to eine Referenz außerhalb des aktuellen Kontextes ist?

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
                }
            ]
        }
    ]
}
FHEM on Raspberry Pi 3B+
CUL868
7x MAX! Thermostat, 8x MAX! Fensterkontakte
Conbee II + deConz, TradFri Lampen, Osram Smart+ Steckdosen

yersinia

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.

Wie betateilchen schreibt, 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...
viele Grüße, yersinia
----
FHEM 6.3 (SVN) on RPi 4B with RasPi OS Bullseye (perl 5.32.1) | FTUI
nanoCUL->2x868(1x ser2net)@tsculfw, 1x433@Sduino | MQTT2 | Tasmota | ESPEasy
VCCU->14xSEC-SCo, 7xCC-RT-DN, 5xLC-Bl1PBU-FM, 3xTC-IT-WM-W-EU, 1xPB-2-WM55, 1xLC-Sw1PBU-FM, 1xES-PMSw1-Pl

KyleK

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 :)

FHEM on Raspberry Pi 3B+
CUL868
7x MAX! Thermostat, 8x MAX! Fensterkontakte
Conbee II + deConz, TradFri Lampen, Osram Smart+ Steckdosen