XSLT-Entsprechung für JSON


14

Ich war daran interessiert, ein XSLT-Äquivalent für JSON zu finden (oder gegebenenfalls zu entwickeln).

Da ich keine gefunden habe, habe ich mir überlegt, welche Abfragesprache für übereinstimmende JSON-Pfade verwendet werden kann, um Vorlagen (aus JavaScript) anzuwenden, wenn eine Übereinstimmung festgestellt wurde (wahrscheinlich nur eine Reihe übereinstimmender Muster in der angegebenen Reihenfolge überprüfen und bei der angehalten erste passende Vorlage, wobei das Äquivalent von xsl: apply-templates berücksichtigt wird, damit die Vorlagen für Kinder erhalten bleiben).

Ich kenne JSONPath, JSONQuery und RQL als JSON-Abfragesprachen (obwohl mir nicht klar war, ob RQL absolute und relative Pfade unterstützt). Anregungen zu zu berücksichtigenden Faktoren und deren relative Vorteile für eine solche Verwendung.


Nur ein zufälliger Gedanke, vielleicht JavaScript und Moustache / Lenker? :)
Knerd

Vielen Dank, aber ich bin mehr daran interessiert, einen Standardansatz zu verwenden (z. B. mindestens einen mit dem Potenzial, da generische JSON-Pfadausdrücke ein allgemein anerkanntes Mittel zum Verweisen auf JSON sind, im Gegensatz zu einer für eine Bibliothek spezifischen Syntax).
Brett Zamir



Ich habe Json -> XML -> XSLT -> Json vor - es funktioniert gut, auch wenn es nicht die effizienteste Lösung ist,
user2813274

Antworten:


27

XML: XSLT :: JSON: x . Was ist x ?

Die einfachste Antwort wäre x = JavaScript. Obwohl Sie dafür ein Argument machen könnten, fühlt es sich unbefriedigend an. Obwohl XSLT technisch vollständig ist , besteht eine schlechte Übereinstimmung zwischen dem deklarativen Stil von XSLT und den imperativeren oder funktionaleren Stilen in JavaScript.

Es gibt einige eigenständige JSON-Abfragesprachen wie JSONPath , JSONiq und RQL, die möglicherweise für den Mittelweg von XML stehen: XPath :: JSON: y (oder möglicherweise XQuery anstelle von XPath). Jede JSON -bezogene Dokumentendatenbank verfügt über eine JSON-bezogene Abfragesprache .

Die Realität ist jedoch, dass es trotz einiger Konkurrenten für die volle XSLT-Position wie SpahQL keine allgemein anerkannten, allgemein unterstützten JSON-Entsprechungen zu XSLT gibt.

Warum?

Warum gibt es bei all dem JSON auf der Welt kein (direkteres) Analogon zu XSLT? Weil viele Entwickler XSLT als fehlgeschlagenes Experiment ansehen. Jede Suchmaschine führt zu Anführungszeichen wie "XSLT ist ein mit Schmerzen behafteter Fehler". Andere haben argumentiert, dass es populärer wäre, wenn es nur besser formatiert wäre. Das Interesse an XSLT ist jedoch im Laufe der Jahre allgemein zurückgegangen . Viele Tools, die dies unterstützen, unterstützen nur Version 1.0 , eine Spezifikation von 1999. 15 Jahre alte Brille? Es gibt eine viel neuere 2.0-Spezifikation, und wenn die Leute von XSLT begeistert wären, würde dies unterstützt. Ist es nicht.

Im Großen und Ganzen haben Entwickler beschlossen, XML-Dokumente mit Code und nicht mit Transformationsvorlagen zu verarbeiten und zu transformieren. Es ist daher nicht überraschend, dass sie sich bei der Arbeit mit JSON im Großen und Ganzen dafür entscheiden würden, dies in ihrer Muttersprache zu tun, anstatt ein zusätzliches "fremdes" Transformationssystem hinzuzufügen.


2
+1, da dies eine nachdenkliche Antwort ist, aber ich denke immer noch, dass es sauberer ist, ein Bündel linear angeordneter Vorlagen mit einer Bibliothek zu haben, die das Durchgehen übernimmt, und obwohl ich denke, dass Sie in Bezug auf die Einstellung zu XSL wahrscheinlich Recht haben (ich würde) Wenn Sie sich an das Camp halten, denken Sie, dass es sich um ein Formatierungsproblem handelt, obwohl der rekursive Stil zugegebenermaßen eine gewisse Anpassung erfordert. Ich wette, ein Teil des Problems liegt in der Trägheit, eine solche Sprache zu entwickeln, um sie zu verwenden (z. B. finde ich) Auch JSONPath selbst benötigt einige Verbesserungen.
Brett Zamir

SpahQL schien keine eigenen Templates zu haben, es scheint also immer noch keine Konkurrenten zu geben, die reines JavaScript oder JSON für den Template-Code (zusammen mit den Datenstrukturen) verwenden, obwohl es Bibliotheken gibt, die HTML als JSON / ausdrücken. JS.
Brett Zamir

1
+1 trotz der Tatsache, dass es etwas an XSLT gibt, das nichts anderes schafft, sich zu replizieren. JSON wird sicherlich eine schwierigere Syntax sein, mit der man ein brauchbares Äquivalent schreiben kann.
user52889

7

Während Jonathan in seiner Antwort hauptsächlich über die Natur von XSLT als Sprache spricht, gibt es meines Erachtens einen anderen Blickwinkel.

Der Zweck von XSLT bestand darin, XML-Dokumente in ein anderes Dokument (XML, HTML, SGML, PDF usw.) umzuwandeln. Auf diese Weise wird XSLT häufig effektiv als Vorlagensprache verwendet.

Es gibt eine Vielzahl von Vorlagenbibliotheken, auch wenn Sie sich auf JavaScript-Bibliotheken beschränken (was Sie nicht benötigen sollten, da sich die JS in JSON nur auf die Entstehung der Notation bezieht und diese JSON nicht implizieren sollte ist nur für JavaScript). Dieser Template-Engine-Selektor gibt Aufschluss über die Vielzahl der verfügbaren JS-Optionen.

In der zweiten Hälfte Ihrer Fragen geht es mehr um Abfragesprachen, und die XML-Version davon ist XPath (nicht XSLT). Wie Sie bemerkt haben, gibt es dort eine Vielzahl von Optionen, und ich kann dieser Liste nichts hinzufügen. Dieser Bereich ist relativ neu, also würde ich vorschlagen, dass Sie sich einen aussuchen und einfach mitmachen.


Im Zweifelsfall finde ich Jonathans Antwort großartig. Ich wollte nur eine alternative Perspektive hinzufügen.
Dancrumb

Ja, faire Punkte (und nochmal: XPath ist das Äquivalent für den zweiten Teil), aber ich bin daran interessiert, dass mein JS XSL (JTLT genannt) einen erweiterten JSONPath verwendet, um JSON auch in eine andere Sprache umzuwandeln (dh HTML als Zeichenfolge oder DOM).
Brett Zamir

Ich habe eine eigene Bibliothek mit dem Namen Jamilih, die ich für das Ausdrücken von rohem HTML als JS / JSON bevorzuge, aber ich brauche etwas Natürliches und hoffe, dass es einprägsam ist für 1) Vorlagen und Pfadübereinstimmung 2) Durchlaufen von APIs, die xsl: apply-templates und xsl entsprechen: Aufrufvorlage (xsl: for-each ist für JS offensichtlich, aber nicht für JSON). Für JS konnte ich Funktionen für Vorlagen und für JSON verwenden (basierend auf Jamilih und diesen iterierenden APIs). Wills ee, wie es geht ...
Brett Zamir

3

Hier einige Beispiele, was Sie mit meinem (kleinen [jslt.min.js] ) JSLT - JavaScript Lightweight Transforms machen können:

https://jsfiddle.net/YSharpLanguage/c7usrpsL/10

( [jslt.min.js] wiegt ~ 3,1 kb, minimiert )

das heißt, nur eine Funktion,

function Per ( subject ) { ... }

... die tatsächlich das Verarbeitungsmodell von XSLT (1.0) nachahmt .

(Vgl. die inneren Funktionen "Transformieren" und "Schablonen" in Per's Körper)

Im Grunde genommen ist es also einfach alles in diese Single function Per ( subject ) { ... }eingebettet, die ihre Bewertung nach der Art ihres (auch) einzigartigen Arguments veranlasst, um Folgendes zu implementieren:

1) Array- Betreff

Knotensatzerstellung / -filterung / -reduzierung / -gruppierung / -reihenfolge / etc , wenn subject ein Array ist, mit dem der resultierende Knotensatz (auch ein Array ) erweitert und an entsprechend benannte Methoden gebunden wird ( nur die zurückgegebene Array- Instanz des Aufrufs an Per ( subjectArray )ist) erweitert (dh Array.prototype bleibt unangetastet)

dh Per :: Array --> Array

(Die Erweiterungsmethoden des resultierenden Arrays haben selbsterklärende Namen wie groupBy, orderBy, flattenBy usw. - siehe die Verwendung in den Beispielen.)

2) Betreff der Zeichenfolge

String-Interpolation , wenn subject ein String ist

( „Pro“ , dann gibt ein Objekt mit einem Verfahren map ( source ), das das Subjekt gebunden ist template string)

dh Per :: String --> {map :: ( AnyValue --> String )}

z.B,

Per("Hi honey, my name is {last}. {first}, {last}.").map({ "first": "James", "last": "Bond" })

ergibt:

"Hi honey, my name is Bond. James, Bond."

während einer von

Per("Those '{*}' are our 10 digits.").map([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])

oder

Per("Those '{*}' are our 10 digits.").map(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

ergibt das gleiche:

"Those '0123456789' are our 10 digits."

aber nur

Per("Those '{*}' are our 10 digits.").map([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], ", ")

Ausbeuten

"Those '0, 1, 2, 3, 4, 5, 6, 7, 8, 9' are our 10 digits."

3) Betreff transformieren

XSLT-Look-Alike-Transformation , wenn das Thema ein Hash mit a ist konventionell definierten "$" -Mitglied ist, das das Array von Umschreiberegeln bereitstellt (und dasselbe wie in (2), gibt "Per" ein Objekt mit einer map ( source )an das Subjekt gebundenen Methode zurück transformieren - wo

"ruleName" in Per ( subjectTransform [ , ruleName ]) ist optional und bietet eine ähnliche Funktionalität wie <xsl: call-template name = "templateName"> ...)

dh Per :: ( Transform [, ruleName :: String ]) -->{map :: ( AnyValue --> AnyValue )}

mit

Transform :: {$ :: Array von Umschreiberegeln [rw.r.] }

( [rw.r.] Prädikat- und Template-Funktionspaare)

zB gegeben (... ein anderes erfundenes Beispiel)

// (A "Member" must have first and last names, and a gender)
function Member(obj) {
  return obj.first && obj.last && obj.sex;
}

var a_transform = { $: [
//...
  [ [ Member ], // (alike <xsl:template match="...">...)
      function(member) {
        return {
          li: Per("{first} {last}").map(member) +
              " " +
              Per(this).map({ gender: member.sex })
        };
      }
  ],

  [ [ function(info) { return info.gender; } ], // (alike <xsl:template match="...">...)
      function(info) { return Per("(gender: {gender})").map(info); }
  ],

  [ [ "betterGenderString" ], // (alike <xsl:template name="betterGenderString">...)
      function(info) {
        info.pronoun = info.pronoun || "his/her";
        return Per("({pronoun} gender is {gender})").map(info);
      }
  ]
//...
] };

dann

Per(a_transform).map({ "first": "John", "last": "Smith", "sex": "Male" })

ergibt:

{ "li": "John Smith (gender: Male)" }

während ... (sehr ähnlich <xsl:call-template name="betterGenderString">...)

"James Bond... " +
Per(a_transform, "betterGenderString").map({ "pronoun": "his", "gender": "Male" })

ergibt:

"James Bond... (his gender is Male)"

und

"Someone... " +
Per(a_transform, "betterGenderString").map({ "gender": "Male or Female" })

ergibt:

"Someone... (his/her gender is Male or Female)"

4) Sonst

die Identitätsfunktion , in allen anderen Fällen

dh Per :: T --> T

(dh Per === function ( value ) { return value ; } )

Hinweis

In (3) oben ist das "this" eines JavaScript im Rumpf einer Template-Funktion somit an die Container / Eigentümer- Transformation und deren Regelsatz (wie durch das Array $: [...] definiert) gebunden. den Ausdruck "Per (this)" in diesem Zusammenhang zu einem funktionsnahen Äquivalent zu XSLTs zu machen

<xsl:apply-templates select="..."/>

"HTH,


1
Das ist ziemlich toll.
Robert Harvey

@RobertHarvey: Abgesehen von der Schärfe von Abschnitt 5.1 an und für sich, die ich vor langer Zeit bemerkt hatte, wurde ich schließlich auch von Evan Lenz 'eingängiger Bemerkung "XSLT ist einfacher als Sie denken!" Unter http: // www fasziniert und inspiriert . lenzconsulting.com/how-xslt-works - und so habe ich mich entschlossen, diese Behauptung (wenn auch nur aus Neugier) in der sehr verständlichen Sprache JavaScript zu überprüfen.
YSharp

Vielen Dank für Ihre ausführliche Antwort. Ich bin mit einigen anderen Dingen beschäftigt (einschließlich meinem eigenen XSLT-Äquivalent), aber ich beabsichtige, darauf zurückzukommen, um genauer hinzuschauen.
Brett Zamir

3

Ich habe kürzlich eine Bibliothek erstellt, json-transforms , genau zu diesem Zweck:

https://github.com/ColinEberhardt/json-transforms

Es verwendet eine Kombination aus JSPath , einem an XPath angelehnten DSL und einem direkt von XSLT inspirierten rekursiven Pattern Matching-Ansatz.

Hier ist ein kurzes Beispiel. Gegeben das folgende JSON-Objekt:

const json = {
  "automobiles": [
    { "maker": "Nissan", "model": "Teana", "year": 2011 },
    { "maker": "Honda", "model": "Jazz", "year": 2010 },
    { "maker": "Honda", "model": "Civic", "year": 2007 },
    { "maker": "Toyota", "model": "Yaris", "year": 2008 },
    { "maker": "Honda", "model": "Accord", "year": 2011 }
  ]
};

Hier ist eine Transformation:

const jsont = require('json-transforms');
const rules = [
  jsont.pathRule(
    '.automobiles{.maker === "Honda"}', d => ({
      Honda: d.runner()
    })
  ),
  jsont.pathRule(
    '.{.maker}', d => ({
      model: d.match.model,
      year: d.match.year
    })
  ),
  jsont.identity
];

const transformed  = jsont.transform(json, rules);

Welche Ausgabe die folgenden:

{
  "Honda": [
    { "model": "Jazz", "year": 2010 },
    { "model": "Civic", "year": 2007 },
    { "model": "Accord", "year": 2011 }
  ]
}

Diese Transformation besteht aus drei Regeln. Das erste entspricht einem Auto, das von Honda hergestellt wurde, wobei ein Objekt mit einer HondaEigenschaft emittiert und dann rekursiv abgeglichen wird. Die zweite Regel ordnet jedem Objekt eine makerEigenschaft zu modelund gibt die yearEigenschaften und aus . Das Finale ist die Identitätstransformation, die rekursiv übereinstimmt.


+1 und danke für die Info. Ich hoffe, dass ich irgendwann mein eigenes github.com/brettz9/jtlt fertigstellen kann, aber es ist hilfreich, mehr Implementierungen zum Vergleichen zu haben.
Brett Zamir

-1

Ich glaube nicht, dass Sie jemals eine JSON-Variante für JSON an sich bekommen werden. Es gibt verschiedene Templating-Engines wie Pythons Jinja2, JavaScripts Nunjucks, die Groovy MarkupTemplateEngine und viele andere, die für Ihre Anforderungen gut geeignet sein sollten. .NET bietet Unterstützung für T4- und JSON-Serialisierung / Deserialisierung.

Da es sich bei den derserialisierten JSON-Daten im Grunde genommen um eine Wörterbuch- oder Map-Struktur handelt, würde dies nur zu Ihrer Template-Engine führen und Sie würden dort die gewünschten Knoten durchlaufen. Die JSON-Daten werden dann von der Vorlage transformiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.