Equivalente XSLT per JSON


14

Ero interessato a trovare (o se necessario sviluppare) un equivalente XSLT per JSON.

Dato che non ne ho trovato nessuno, stavo considerando il possibile linguaggio di query da utilizzare per la corrispondenza dei percorsi JSON in modo da applicare i modelli (da JavaScript) in caso di corrispondenza (probabilmente solo verificando una serie di schemi di corrispondenza in ordine e fermandomi al primo modello che corrisponde, pur consentendo l'equivalente di xsl: apply-templates per mantenere attivi i modelli per i bambini).

Sono a conoscenza di JSONPath, JSONQuery e RQL come linguaggi di query JSON (anche se non ero del tutto chiaro se RQL supportasse percorsi assoluti e relativi). Eventuali suggerimenti su fattori da considerare e relativi vantaggi di ciascuno nei confronti di tale utilizzo.


Solo un pensiero casuale, forse JavaScript e Moustache / Manubri? :)
Knerd,

Grazie, ma sono più interessato all'utilizzo di un approccio standard (ad esempio, almeno uno con il potenziale, dato che le espressioni di percorso JSON generiche sarebbero un mezzo genericamente riconosciuto di riferimento a JSON rispetto a una sintassi specifica di una libreria).
Brett Zamir,


1
Ho anche trovato questo interessante: json-template.googlecode.com/svn/trunk/doc/…
Robert Harvey,

Ho fatto Json -> XML -> XSLT -> Json prima - funziona benissimo, anche se non è la soluzione più efficiente,
user2813274

Risposte:


27

XML: XSLT :: JSON: x . Che cos'è x ?

La risposta più semplice sarebbe x = JavaScript. Anche se potresti fare un caso per questo, sembra insoddisfacente. Sebbene XSLT sia tecnicamente completo di Turing , c'è una scarsa corrispondenza tra lo stile dichiarativo di XSLT e gli stili più imperativi o funzionali visti in JavaScript.

Esistono alcuni linguaggi di query JSON autonomi, come JSONPath , JSONiq e RQL che potrebbero sostituire la via di mezzo di XML: XPath :: JSON: y (o, eventualmente, XQuery anziché XPath). E ogni database di documenti incentrato su JSON ha un linguaggio di query correlato a JSON .

Ma la realtà è che, nonostante esistano alcuni contendenti per l'intera posizione XSLT, come SpahQL , non esistono equivalenti JSON generalmente accettati e ampiamente supportati da XSLT.

Perché?

Con tutto il JSON al mondo, perché non esiste un analogo (più diretto) a XSLT? Perché molti sviluppatori vedono XSLT come un esperimento fallito. Qualsiasi motore di ricerca porterà a citazioni come "XSLT è un fallimento avvolto nel dolore". Altri hanno sostenuto che se fosse solo meglio formattato, sarebbe più popolare. Ma l' interesse per XSLT è generalmente diminuito nel corso degli anni . Molti strumenti che lo supportano supportano solo la versione 1.0 , che è una specifica del 1999. Specifiche di quindici anni? C'è una specifica 2.0 molto più recente e se le persone fossero entusiaste di XSLT, sarebbero supportate. Non lo è.

In generale, gli sviluppatori hanno scelto di elaborare e trasformare documenti XML con codice, non modelli di trasformazione. Non sorprende quindi che quando lavorano con JSON, in genere scelgono di farlo nella loro lingua madre, piuttosto che aggiungere un ulteriore sistema di trasformazione "straniero".


2
+1 in quanto questa è una risposta ponderata, ma penso ancora che sia più pulito avere un sacco di modelli disposti in modo lineare con una libreria che fa il passo, e mentre penso che tu abbia probabilmente ragione sull'atteggiamento verso XSL ( propenso al campo pensando che sia un problema di formattazione sebbene lo stile ricorsivo abbia certamente bisogno di un po 'di personalizzazione), scommetto che alcuni dei problemi potrebbero essere l'inerzia nel bisogno di sviluppare un tale linguaggio per usarlo (ad esempio, sto trovando anche JSONPath stesso ha bisogno di alcuni miglioramenti).
Brett Zamir,

SpahQL non sembrava avere i propri modelli, quindi sembra che non ci siano contendenti che utilizzano effettivamente JavaScript o JSON puri per il codice del modello (insieme alle strutture di dati) anche se ci sono librerie che consentono l'espressione di HTML come JSON / JS.
Brett Zamir,

1
+1 nonostante il fatto che ci sia qualcosa in XSLT che nient'altro riesce a replicare. JSON sarà sicuramente una sintassi più difficile con cui scrivere un equivalente utilizzabile.
user52889,

7

Mentre Jonathan parla in gran parte della natura dell'XSLT come linguaggio nella sua risposta, penso che ci sia un'altra prospettiva da considerare.

Lo scopo di XSLT era trasformare i documenti XML in qualche altro documento (XML, HTML, SGML, PDF, ecc.). In questo modo, XSLT viene spesso utilizzato, in modo efficace, come linguaggio modello.

Esiste una vasta gamma di librerie di modelli là fuori, anche se ti limiti alle librerie JavaScript (cosa che non dovresti avere bisogno, poiché JS in JSON si riferisce solo alla genesi della notazione e non dovrebbe essere preso per implicare che JSON è solo per JavaScript). Questo selettore motore modello fornisce e indica la varietà di opzioni JS disponibili.

L'ultima metà delle domande parla di più sui linguaggi di query e la versione XML di questi sarebbe XPath (non XSLT). Come hai notato, ci sono una varietà di opzioni lì e non ho nulla da aggiungere a quell'elenco. Questa area è relativamente nuova, quindi ti suggerisco di sceglierne una e di seguirla.


In caso di dubbi, penso che la risposta di Jonathan sia ottima; Volevo solo aggiungere una prospettiva alternativa.
Dancrumb,

Sì, punti giusti (e sì in merito: XPath è l'equivalente per la seconda parte), ma sono interessato a vedere il mio JS XSL (chiamandolo JTLT) utilizzare un JSONPath migliorato che trasforma anche JSON in un'altra lingua (ovvero HTML come stringa o DOM).
Brett Zamir,

Ho la mia libreria chiamata Jamilih che preferisco per esprimere HTML grezzo come JS / JSON, ma ho bisogno di qualcosa che mi sembri naturale e spero accattivante per 1) Modelli e abbinamento dei percorsi 2) Iterare API equivalenti a xsl: apply-templates e xsl: call-template (xsl: for-each è ovvio per JS, ma non JSON). Per JS, potrei usare le funzioni per i template e per JSON (basato su Jamilih e quelle API iteranti). Volontà come va ...
Brett Zamir,

3

Ecco alcuni esempi di cosa puoi fare con la mia (piccola [jslt.min.js] ) JSLT - trasformazioni leggere JavaScript:

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

( [jslt.min.js] pesa ~ 3.1kb minimizzato )

cioè solo una funzione,

function Per ( subject ) { ... }

... che in realtà imita il modello di elaborazione di XSLT (1.0) .

(cfr. le funzioni interne "trasforma" e "modello", nel corpo di Per)

Quindi, in sostanza, è semplicemente tutto racchiuso in quel singolo function Per ( subject ) { ... }che forgia la sua valutazione sul tipo del suo (anche) unico argomento, per implementare:

1) Oggetto dell'array

creazione / filtraggio / appiattimento / raggruppamento / ordinamento di nodi del nodo , se il soggetto è un array, in cui il nodo risultante (anche un array ) viene esteso e associato a metodi di conseguenza definiti ( solo l' istanza di array restituita della chiamata a Per ( subjectArray )è esteso; ovvero, Array.prototype non viene toccato)

vale a dire, Per :: Array --> Array

(i metodi di estensione dell'array risultanti che hanno nomi autoesplicativi come groupBy, orderBy, flattenBy, ecc. - cfr. l'uso negli esempi)

2) Oggetto della stringa

interpolazione di stringhe , se soggetto è una stringa

("Per" restituisce quindi un oggetto con un metodo map ( source ), che è associato alla stringa del modello soggetto )

vale a dire, Per :: String --> {map :: ( AnyValue --> String )}

per esempio,

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

rendimenti:

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

mentre uno dei

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

o

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

produce lo stesso:

"Those '0123456789' are our 10 digits."

ma solo

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

i rendimenti

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

3) Trasforma soggetto

Trasformazione simile a XSLT , se l'oggetto è un hash con un membro "$" convenzionalmente definito che fornisce l'array di regole di riscrittura (e lo stesso di in (2), "Per" quindi restituisce un oggetto con un metodo map ( source )associato al soggetto trasforma - dove

"ruleName" in Per ( subjectTransform [ , ruleName ])è facoltativo e fornisce funzionalità simili a <xsl: call-template name = "templateName"> ...)

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

con

Trasforma :: {$ :: Matrice di regole di riscrittura [rw.r.] }

( [rw.r.] predicato e coppie di funzioni modello)

ad esempio, dato (... un altro esempio inventato)

// (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);
      }
  ]
//...
] };

poi

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

rendimenti:

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

mentre ... (molto simile <xsl:call-template name="betterGenderString">...)

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

rendimenti:

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

e

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

rendimenti:

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

4) Altrimenti

la funzione identità , in tutti gli altri casi

vale a dire, Per :: T --> T

(cioè, Per === function ( value ) { return value ; })

Nota

in (3) sopra, un "questo" di JavaScript nel corpo di una funzione modello è quindi associato alla trasformazione contenitore / proprietario e al suo insieme di regole (come definito dalla matrice $: [...]) - pertanto, rendendo l'espressione "Per (questo)", in quel contesto, un equivalente funzionalmente vicino agli XSLT

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

'HTH,


1
È abbastanza bello.
Robert Harvey,

@RobertHarvey: oltre alla complessità della sezione 5.1 in sé e per sé che avevo notato molto tempo fa, alla fine mi sono anche incuriosito e ispirato dall'osservazione accattivante di Evan Lenz "XSLT è più semplice di quanto pensi!", Su http: // www. lenzconsulting.com/how-xslt-works - e così ho deciso di provare a verificare quell'affermazione (se non altro per curiosità) nel linguaggio molto malleabile che è JavaScript.
YSharp

Grazie mille per la tua risposta dettagliata. Sono impegnato in altre cose (incluso il mio equivalente XSLT), ma ho intenzione di tornare su questo per dare un'occhiata più attenta.
Brett Zamir,

3

Di recente ho creato una libreria, json-transforms , proprio per questo scopo:

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

Utilizza una combinazione di JSPath , una DSL modellata su XPath e un approccio ricorsivo di pattern matching, ispirato direttamente da XSLT.

Ecco un breve esempio. Dato il seguente oggetto JSON:

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 }
  ]
};

Ecco una trasformazione:

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

Che produce il seguente:

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

Questa trasformazione è composta da tre regole. Il primo corrisponde a qualsiasi automobile prodotta da Honda, emettendo un oggetto con una Hondaproprietà, quindi abbinando ricorsivamente. La seconda regola corrisponde a qualsiasi oggetto con una makerproprietà, producendo le proprietà modele year. Il finale è la trasformazione dell'identità che corrisponde in modo ricorsivo.


+1 e grazie per le informazioni. Spero di completare il mio github.com/brettz9/jtlt ad un certo punto, ma è utile avere più implementazioni da confrontare.
Brett Zamir,

-1

Non credo che otterrai mai una variante JSON per JSON di per sé. Esistono diversi motori di template come Jinja2 di Python, JavaScripts Nunjucks, Groovy MarkupTemplateEngine e molti altri che dovrebbero essere adatti a ciò che desideri. .NET ha il supporto per la serializzazione / deserializzazione T4 e JSON, quindi anche questo.

Dato che i dati JSON dederializzati sarebbero fondamentalmente un dizionario o una struttura di mappe, ciò passerebbe semplicemente al tuo motore di template e ti passeresti sui nodi desiderati lì. I dati JSON vengono quindi trasformati dal modello.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.