Vorlagensprache für JSON-Daten
Wenn Sie die meiste Arbeit mit der JSON-Transformation verbringen, benötigen Sie möglicherweise diese Vorlagensprache, um effektiv zu sein.
Motivation
Wir ( Ruderstack ) sind eine Integrationsplattform und unterstützen über 200 Integrationen. Wir haben diese Integrationen mithilfe von nativem Javascript- Code implementiert, um eingehende Ereignisse in Zielnutzdaten umzuwandeln. Zusammenfassend handelt es sich also um eine JSON-Datenmanipulation. Die Aufrechterhaltung all dieser Integrationen ist eine Herausforderung. Deshalb haben wir jsonata untersucht , um weniger Code für die Transformation von JSON-Daten zu schreiben. Obwohl diese Bibliothek ausgezeichnet ist, müssen wir dennoch unsere Leistungsanforderungen erfüllen. JSONata analysiert beispielsweise die angegebene Vorlage, erstellt einen Abstract Syntax Tree (AST) und interpretiert den AST für die angegebene Eingabe. Da wir den AST jedes Mal durchlaufen müssen, ist er langsam. Deshalb wollten wir eine Template-Engine erstellen, die Javascript-Code generiert, damit während der Laufzeit weniger Overhead entsteht.
Überblick
Eine Bibliothek zur Verarbeitung von JSON-Daten mithilfe einer benutzerdefinierten Syntax, die auf Javascript und jspath basiert . Wir danken den jspath-Autoren für ihre hervorragende Arbeit, da unsere Bibliothek eine Erweiterung der Originalbibliothek ist. Wir möchten auch dem IBM- Team für seine Arbeit an jsonata danken , da wir mehrere Ideen aus der Bibliothek übernommen haben. Sie können unsere Bibliothek auch als Alternative zu jsonata in Betracht ziehen .
Diese Bibliothek generiert aus der Vorlage einen Javascript-Funktionscode und nutzt die Funktion dann zur Auswertung der JSON-Daten. Es gibt den Javascript-Code in den folgenden Schritten aus:
- Lexing (Tokenisierung)
- Parsen (AST-Erstellung)
- Übersetzung (Codegenerierung)
- Die Engine- Klasse abstrahiert die oben genannten Schritte und bietet eine praktische Möglichkeit, die JSON-Vorlagen zum Auswerten der Eingaben zu verwenden.
Installieren:npm install rudder-json-template-engine
const engine = new JsonTemplateEngine(`'Hello ' + .name`);
engine.evaluate({name: 'World'}); // => Hello World
Die Vorlage besteht aus einer Reihe von Anweisungen, und das Ergebnis der letzten Anweisung ist die Ausgabe der Vorlage.
Variablen
const a = 1
let b = a + 2
a + b
Eingabe bezieht sich auf das JSON-Dokument, das wir mithilfe einer Vorlage verarbeiten möchten. Bindungen beziehen sich auf zusätzliche Daten oder Funktionen, die wir zur effizienten Verarbeitung der Daten bereitstellen würden.
Beispiele:
- Vorlage:
"Hello " + (.name ?? $.defaultName) - Auswertung:
engine.evaluate({name: 'World'}, {defaultName: 'World'}); {name: 'World'}ist Eingabe.^.namebezieht sich auf die Eigenschaft „name“ der Eingabe. Wir können uns auch.nameauf dasselbe beziehen.^bezieht sich immer auf den Stamm der Eingabe und.auf den aktuellen Kontext. Für mehr Klarheit sehen Sie sich das Beispiel an.{defaultName: 'World'}ist Bindungen.$.defaultNamebezieht sich auf die Eigenschaft „defaultName“ der Bindungen. Für mehr Klarheit sehen Sie sich das Beispiel an.
let arr = [1, 2, 3, 4]
let a = arr[1, 2] // [2, 3]
let b = arr[0:2] // [1, 2]
let c = arr[-2:] // [3, 4]
Objekte
let key = "some key"
// { "a": 1, "b": 2, "c": 3, "some key": 4 }
let obj = {a: 1, b: 2, c: 3, [key]: 4 }
let a = obj["a"] // 1
let b = obj.a // 1
let c = obj{["a", "b"]} // { "a": 1, "b": 2}
let d = obj{~["a", "b"]} // { "c": 3, "some key": 4}
Funktionen
Normale Funktionen
let fn = function(arg1, arg2){
arg1 + arg2
}
Lambda/Short-Funktionen
let fn = array.map(lambda 2 * ?0);
let fn = array.map(function(args) {
2 * args[0]
})
Asynchrone Funktionen
let fn = async function(arg1, arg2){
const result = await doSomethingAsync(arg1, arg2)
doSomethingSync(result)
}
let result = await doSomething(.a, .b)
inputPfade werden verwendet, um auf Eigenschaften in , bindingsund zuzugreifen variables.
Einfache Wege
Einfache Pfade unterstützen eingeschränkte Pfadfunktionen und werden als direkte Eigenschaftenzugriffsanweisungen im generierten Javascript-Code übersetzt. a.b.cwerden übersetzt, sodass sie im Vergleich zu Rich-Pfadena?.b?.c sehr schnell sind . Einfache Pfade sind ideal, wenn wir die Objektstruktur kennen.
Unterstützte Funktionen:
- Einfache Selektoren
- Einzelindexfilter Für mehr Klarheit sehen Sie sich das Beispiel an.
Umfangreiche Pfade werden in komplexen Code umgewandelt, um verschiedene Variationen der Daten zu unterstützen.
Wenn wir einen Rich-Pfad für expression: verwenden a.b.c, werden die folgenden Variationen automatisch verarbeitet.
[{"a": { "b": [{"c": 2}]}}]{"a": { "b": [{"c": 2}]}}{"a": [{ "b": [{"c": 2}]}]}- Behandelt automatisch die Auswahl aus verschachtelten Objekten und Arrays.
let x = a.b.c;
let y = a."some key".c
Platzhalterselektoren
a.*.c // selects c from any direct property of a
Nachkommenselektoren
// selects c from any child property of a
// a.b.c, a.b1.b2.c or a.b1.b2.b3.c
let x = a..c;
let y = a.."some key";
Einzelne Index- oder Eigenschaftsfilter
let x = a[0].c;
let y = a[-1].c; // selects last element from array
let z = a["some key"].c
Mehrere Indizes oder Eigenschaftenfilter
let x = a[0, 2, 5].c;
let y = a["some key1", "some key2"].c;
Bereichsfilter
let x = a[2:5].c;
let y = a[:-2].c;
let z = a[2:].c;
let x = obj{["a", "b"]}; // selects a and b
let y = obj{~["a", "b"]}; // selects all properties except a and b
Bedingte oder Objektfilter
let x = obj{.a > 1};
Blockausdrücke
let x = obj.({
a: .a + 1,
b: .b + 2
});
let x = obj.([.a+1, .b+2]);
Kontextvariablen
.orders@order#idx.products.({
name: .name,
price: .price,
orderNum: idx,
orderId: order.id
})
Pfadoptionen
Wir können defaultPathType beim Erstellen einer Engine-Instanz erwähnen.
// For using simple path as default path type
// a.b.c will be treated as simple path
JsonTemplateEngine.create(`a.b.c`, {defaultPathType: PathType.SIMPLE});
// For using rich path as default path type
// a.b.c will be treated as rich path
JsonTemplateEngine.create(`a.b.c`, {defaultPathType: PathType.RICH});
// Use ~s to treat a.b.c as simple path
~s a.b.c
// Use ~r to treat a.b.c as rich path
~r a.b.c
Zeitausdrücke kompilieren
Kompilierzeitausdrücke werden während der Kompilierungsphase mithilfe der Kompilierzeitbindungsoption ausgewertet.
// {{$.a.b.c}} gets translated to 1 and
// final translated code will be "let a = 1;"
JsonTemplateEngine.create(`let a = {{$.a.b.c}};`, {
compileTimeBindings: {
a: {
b: {
c: 1
}
}
}
});
Abschluss
Wir haben eine einfache Sprache zur effizienten Verarbeitung von JSON-Daten erstellt und sie als Open Source bereitgestellt . Es hat Spaß gemacht, eine Sprache zu entwickeln, und ich hoffe, Sie haben heute etwas Neues gelernt.

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































