2.1. Overview (Überblick)
2.1. Overview (Überblick)
Ein JSONPath-expression (Ausdruck) ist eine Zeichenkette, die, angewendet auf einen JSON-Wert (das query argument / Abfrageargument), null oder mehr Knoten des Arguments auswählt und diese Knoten als Knotenliste ausgibt.
Eine Abfrage MUSS mit UTF-8 kodiert werden. Die in diesem Dokument angegebene Grammatik für Abfragen setzt voraus, dass deren UTF-8-Form zuerst in Unicode-Skalare gemäß [RFC3629] decodiert wird; Implementierungsansätze, die zu einem äquivalenten Ergebnis führen, sind möglich.
Eine als JSONPath-Abfrage zu verwendende Zeichenkette muss well-formed (wohlgeformt) und valid (gültig) sein. Eine Zeichenkette ist eine wohlgeformte JSONPath-Abfrage, wenn sie der ABNF-Syntax in diesem Dokument entspricht. Eine wohlgeformte JSONPath-Abfrage ist gültig, wenn sie außerdem beide semantischen Anforderungen dieses Dokuments erfüllt, und zwar:
-
Ganze Zahlen in der JSONPath-Abfrage, die für die JSONPath-Verarbeitung relevant sind (z. B. Indexwerte und Schritte), MÜSSEN im Bereich exakter ganzzahliger Werte von Internet JSON (I-JSON) liegen (siehe Abschnitt 2.2 von [RFC7493]), nämlich im Intervall [-(2^53)+1, (2^53)-1].
-
Verwendungen von Funktionserweiterungen MÜSSEN well-typed (wohlgetypt) sein, wie in Abschnitt 2.4.3 beschrieben.
Eine JSONPath-Implementierung MUSS bei jeder Abfrage, die nicht wohlgeformt und gültig ist, einen Fehler melden. Wohlgeformtheit und Gültigkeit von JSONPath-Abfragen sind unabhängig vom JSON-Wert, auf den die Abfrage angewendet wird. Weitere Fehler bezüglich Wohlgeformtheit und Gültigkeit einer JSONPath-Abfrage dürfen bei der Anwendung der Abfrage auf einen Wert nicht auftreten. Damit werden Wohlgeformtheits-/Gültigkeitsfehler in der Abfrage klar von Diskrepanzen getrennt, die tatsächlich aus Datenmängeln stammen können.
Diskrepanzen zwischen der von einer gültigen Abfrage erwarteten Struktur und der in den Daten gefundenen Struktur können zu leeren Abfrageergebnissen führen, die unerwartet sein und auf Fehler in der einen oder anderen Form hindeuten. JSONPath-Implementierungen können daher Diagnoseausgaben für Anwendungsentwickler bereitstellen, die die Ursache leerer Ergebnisse finden helfen.
Natürlich kann eine Implementierung bei der Ausführung einer JSONPath-Abfrage dennoch fehlschlagen, z. B. wegen Ressourcenerschöpfung, aber das wird in diesem Dokument nicht modelliert. Die Implementierung DARF jedoch nicht still fehlerhaft arbeiten. Insbesondere MUSS die Implementierung, wenn eine gültige JSONPath-Auswertung gegen einen strukturierten Wert erfolgt, dessen Größe zu groß ist, um die Abfrage korrekt zu verarbeiten (z. B. weil Zahlen außerhalb des Bereichs exakter Werte verarbeitet werden müssten), einen Hinweis auf einen Überlauf liefern.
(Leser, die mit dem HTTP-Fehlermodell vertraut sind, mögen bei Wohlgeformtheit und Gültigkeit an Fehler vom Typ 400 denken und Ressourcenerschöpfung sowie verwandte Fehler als vergleichbar mit Fehlern vom Typ 500 erkennen.)
2.1.1. Syntax
Syntaktisch besteht eine JSONPath-Abfrage aus einem Wurzel-Identifier ($), der für eine Knotenliste steht, die den Wurzelknoten des Abfragearguments enthält, gefolgt von einer möglicherweise leeren Sequenz von segments (Segmenten).
jsonpath-query = root-identifier segments
segments = *(S segment)
B = %x20 / ; Space
%x09 / ; Horizontal tab
%x0A / ; Line feed or New line
%x0D ; Carriage return
S = *B ; optional blank space
Syntax und Semantik der Segmente sind in Abschnitt 2.5 definiert.
2.1.2. Semantics (Semantik)
In diesem Dokument definieren die Semantik einer JSONPath-Abfrage die erforderlichen Ergebnisse und schreiben die interne Arbeitsweise einer Implementierung nicht vor. Das Dokument kann Semantik prozedural Schritt für Schritt beschreiben; solche Beschreibungen sind jedoch nur in dem Sinne normativ, dass jede Implementierung ein identisches Ergebnis liefern MUSS, nicht in dem Sinne, dass Implementierer dieselben Algorithmen verwenden müssen.
Die Semantik ist: Eine gültige Abfrage wird gegen einen Wert (das Abfrageargument) ausgeführt und erzeugt eine Knotenliste (d. h. eine Liste von null oder mehr Knoten des Werts).
Die Abfrage ist ein Wurzel-Identifier gefolgt von einer Sequenz von null oder mehr Segmenten, von denen jedes auf das Ergebnis des vorherigen Wurzel-Identifiers oder Segments angewendet wird und Eingabe für das nächste Segment liefert. Diese Ergebnisse und Eingaben haben die Form von Knotenlisten.
Die aus dem Wurzel-Identifier resultierende Knotenliste enthält einen einzelnen Knoten (das Abfrageargument). Die aus dem letzten Segment resultierende Knotenliste wird als Ergebnis der Abfrage präsentiert. Je nach konkreter API kann sie als Array der JSON-Werte an den Knoten, als Array von Normalized Paths, die die Knoten referenzieren, oder beides – oder eine andere vom Wunsch der Implementierung abhängige Darstellung – präsentiert werden. Hinweis: Eine leere Knotenliste ist ein gültiges Abfrageergebnis.
Ein Segment arbeitet nacheinander auf jedem Knoten seiner Eingabe-Knotenliste, und die resultierenden Knotenlisten werden in der Reihenfolge der Eingabe-Knotenliste, aus der sie stammen, verkettet, um das Segmentergebnis zu erzeugen. Ein Knoten kann mehr als einmal ausgewählt werden und erscheint dann entsprechend oft in der Knotenliste. Doppelte Knoten werden nicht entfernt.
Ein syntaktisch gültiges Segment DARF bei der Abfrageausführung keine Fehler erzeugen. Das bedeutet, dass einige als fehlerhaft erscheinende Operationen, wie die Verwendung eines Index außerhalb des Array-Bereichs, einfach dazu führen, dass weniger Knoten ausgewählt werden. (Weitere Diskussion dieser Eigenschaft findet sich in der Einleitung von Abschnitt 2.1.)
Als Konsequenz: Wenn eines der Segmente eine leere Knotenliste erzeugt, erzeugt die gesamte Abfrage eine leere Knotenliste.
Wenn die Semantik einer Abfrage einer Implementierung die Wahl zwischen mehreren möglichen Ordnungen lässt, kann eine bestimmte Implementierung bei aufeinanderfolgenden Läufen der Abfrage unterschiedliche Ordnungen erzeugen.
2.1.3. Example (Beispiel)
Betrachten Sie dieses Beispiel. Mit dem Abfrageargument {"a":[{"b":0},{"b":1},{"c":2}]} wählt die Abfrage $.a[*].b die folgende Liste von Knoten (hier durch ihre Werte bezeichnet): 0, 1.
Die Abfrage besteht aus $ gefolgt von drei Segmenten: .a, [*] und .b.
Zuerst erzeugt $ eine Knotenliste, die nur das Abfrageargument enthält.
Als Nächstes wählt .a aus jedem Objekt-Eingabeknoten den Knoten des Mitgliedswerts mit dem Mitgliedsnamen „a“. Das Ergebnis ist wieder eine Liste mit einem einzigen Knoten: [{"b":0},{"b":1},{"c":2}].
Dann wählt [*] alle Elemente aus dem Eingabe-Array-Knoten. Das Ergebnis ist eine Liste von drei Knoten: {"b":0}, {"b":1} und {"c":2}.
Schließlich wählt .b aus jedem Objekt-Eingabeknoten mit Mitgliedsnamen b den Knoten des zugehörigen Mitgliedswerts. Das Ergebnis ist eine Liste mit 0, 1. Das ist die Verkettung von drei Listen: zwei der Länge eins mit 0 bzw. 1 und eine der Länge null.