Équivalent XSLT pour JSON

15

Je souhaitais trouver (ou si nécessaire développer) un équivalent XSLT pour JSON.

Comme je n'en ai pas trouvé, je considérais le langage de requête possible à utiliser pour faire correspondre les chemins JSON afin d'appliquer des modèles (à partir de JavaScript) quand il y avait une correspondance (probablement juste vérifier un tableau de modèles correspondants dans l'ordre, et s'arrêter à la premier modèle qui correspond, tout en permettant l'équivalent de xsl: apply-templates pour garder les modèles en cours pour les enfants).

Je connais JSONPath, JSONQuery et RQL en tant que langages de requête JSON (même si je ne savais pas très bien si RQL supportait les chemins absolus et relatifs). Toutes suggestions sur les facteurs à considérer et les avantages relatifs de chacun vers une telle utilisation.

Brett Zamir
la source
Juste une pensée aléatoire, JavaScript et Moustache / Guidon peut-être? :)
Knerd
Merci, mais je préfère utiliser une approche standard (par exemple, au moins une avec le potentiel, étant donné que les expressions génériques de chemin JSON seraient un moyen génériquement reconnu de référencer JSON par opposition à une syntaxe spécifique à une bibliothèque).
Brett Zamir
1
J'ai aussi trouvé cela intéressant: json-template.googlecode.com/svn/trunk/doc/…
Robert Harvey
J'ai fait Json -> XML -> XSLT -> Json avant - cela fonctionne bien, même si ce n'est pas la solution la plus efficace,
user2813274

Réponses:

27

XML: XSLT :: JSON: x . Qu'est-ce que x ?

La réponse la plus simple serait x = JavaScript. Bien que vous puissiez justifier cela, cela ne vous semble pas satisfaisant. Même si XSLT est techniquement complet , il existe une faible correspondance entre le style déclaratif de XSLT et les styles plus impératifs ou fonctionnels vus en JavaScript.

Il existe quelques langages de requête JSON autonomes, comme JSONPath , JSONiq et RQL, qui pourraient remplacer le milieu de XML: XPath :: JSON: y (ou peut-être XQuery plutôt que XPath). Et chaque base de données de documents axée sur JSON possède un langage de requête lié à JSON .

Mais la réalité est que, bien qu'il y ait quelques prétendants au poste XSLT complet, comme SpahQL , il n'y a pas d'équivalent JSON généralement accepté et largement pris en charge par XSLT.

Pourquoi?

Avec tout le JSON dans le monde, pourquoi n'y a- t - il pas un analogue (plus direct) du XSLT? Parce que de nombreux développeurs considèrent XSLT comme une expérience ayant échoué. Tout moteur de recherche mènera à des citations comme «XSLT est un échec enveloppé de douleur». D'autres ont fait valoir que s'il était simplement mieux formaté, il serait plus populaire. Mais l' intérêt pour XSLT a généralement diminué au fil des ans . De nombreux outils qui le prennent en charge ne prennent en charge que la version 1.0 , qui est une spécification de 1999. Spécifications de quinze ans? Il existe une spécification 2.0 beaucoup plus récente, et si les gens étaient enthousiastes à propos de XSLT, elle serait prise en charge. Ça ne l'est pas.

En gros, les développeurs ont choisi de traiter et de transformer les documents XML avec du code, pas des modèles de transformation. Il n'est donc pas surprenant qu'en travaillant avec JSON, ils choisissent généralement de le faire dans leur langue maternelle, plutôt que d'ajouter un système de transformation "étranger" supplémentaire.

Jonathan Eunice
la source
2
+1 car c'est une réponse réfléchie, mais je pense toujours qu'il est plus propre d'avoir un tas de modèles disposés de façon linéaire avec une bibliothèque qui fait le pas, et même si je pense que vous avez probablement raison sur l'attitude envers XSL (je pencher pour le camp en pensant qu'il s'agit d'un problème de formatage bien que le style récursif ait certes besoin d'une certaine personnalisation), je parierais qu'une partie du problème peut être l'inertie dans le besoin de développer un tel langage pour l'utiliser (par exemple, je trouve même JSONPath lui-même a besoin de quelques améliorations).
Brett Zamir
SpahQL ne semblait pas avoir ses propres modèles, il semble donc qu'il n'y ait pas de prétendants qui utilisent réellement du JavaScript ou du JSON pur pour le code du modèle (ainsi que les structures de données), même s'il existe des bibliothèques qui permettent l'expression de HTML en tant que JSON / JS.
Brett Zamir
1
+1 malgré le fait qu'il existe quelque chose à propos de XSLT que rien d'autre ne parvient à reproduire. JSON va certainement être une syntaxe plus difficile à écrire avec un équivalent utilisable.
user52889
7

Alors que Jonathan parle largement de la nature du XSLT en tant que langage dans sa réponse, je pense qu'il y a un autre angle à considérer.

Le but de XSLT était de transformer des documents XML en un autre document (XML, HTML, SGML, PDF, etc.). De cette façon, XSLT est fréquemment utilisé, efficacement, comme langage de modèle.

Il existe un large éventail de bibliothèques de modèles, même si vous vous limitez aux bibliothèques JavaScript (dont vous ne devriez pas avoir besoin, car le JS dans JSON ne fait référence qu'à la genèse de la notation et ne doit pas être interprété comme impliquant JSON est uniquement pour JavaScript). Ce sélecteur de moteur de modèle donne et indique la variété des options JS disponibles.

La seconde moitié de vos questions parle davantage des langages de requête et la version XML de ceux-ci serait XPath (pas XSLT). Comme vous l'avez noté, il existe une variété d'options et je n'ai rien à ajouter à cette liste. Ce domaine est relativement nouveau, je vous suggère donc d'en choisir un et d'y aller.

Dancrumb
la source
En cas de doute, je pense que la réponse de Jonathan est excellente; Je voulais juste ajouter une perspective alternative.
Dancrumb
Oui, juste points (et oui re: XPath étant l'équivalent pour la deuxième partie), mais je suis intéressé à voir mon JS XSL (l'appeler JTLT) utiliser un JSONPath amélioré transformer JSON en un autre langage aussi (c'est-à-dire HTML comme chaîne ou DOM).
Brett Zamir
J'ai ma propre bibliothèque appelée Jamilih que je préfère pour exprimer le HTML brut en JS / JSON, mais j'ai besoin de quelque chose de naturel et j'espère accrocheur pour 1) Modèles et correspondance de chemin 2) Itérations d'API équivalentes à xsl: apply-templates et xsl: modèle d'appel (xsl: for-each est évident pour JS, mais pas JSON). Pour JS, je pouvais utiliser des fonctions pour les modèles et pour JSON (basé sur Jamilih et ces API itératives). Will ee comment ça se passe ...
Brett Zamir
3

Voici quelques exemples de ce que vous pouvez faire avec mon (petit [jslt.min.js] ) JSLT - Transformations légères JavaScript:

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

( [jslt.min.js] pèse ~ 3,1 ko minifié )

c'est-à-dire une seule fonction,

function Per ( subject ) { ... }

... qui imite en fait le modèle de traitement de XSLT (1.0) .

(cf. les fonctions internes "transform" et "template", dans le corps de Per)

Donc, en substance, c'est tout simplement tout cuit dans ce single function Per ( subject ) { ... }qui forge son évaluation sur le type de son argument (également) unique, à implémenter, soit:

1) Sujet du tableau

création / filtrage de l'ensemble de nœuds / aplatissement / regroupement / classement / etc , si l'objet est un tableau, où l'ensemble de nœuds résultant (un tableau également) est étendu avec et lié aux méthodes nommées en conséquence ( seule l' instance de tableau renvoyée de l'appel à Per ( subjectArray )est étendu; c.-à-d., Array.prototype n'est pas modifié)

c'est-à-dire Per :: Array --> Array

(les méthodes d'extension du tableau résultant ayant des noms explicites tels que, groupBy, orderBy, flattenBy, etc. - cf. l'utilisation dans les exemples)

2) Sujet de chaîne

interpolation de chaîne , si le sujet est une chaîne

("Per" renvoie ensuite un objet avec une méthode map ( source ), qui est liée à la chaîne de modèle objet )

c'est-à-dire, Per :: String --> {map :: ( AnyValue --> String )}

par exemple,

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

rendements:

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

tandis que

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

ou

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

donne le même:

"Those '0123456789' are our 10 digits."

mais, seulement

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

les rendements

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

3) Transformer le sujet

Transformation similaire à XSLT , si le sujet est un hachage avec un membre "$" défini de manière conventionnelle fournissant le tableau de règles de réécriture (et comme dans (2), "Per" renvoie ensuite un objet avec une méthode map ( source )liée au sujet transformer - où

"ruleName" dans Per ( subjectTransform [ , ruleName ])est facultatif et fournit des fonctionnalités similaires à <xsl: call-template name = "templateName"> ...)

c'est-à-dire Per :: ( Transform [, ruleName :: String ]) -->{map :: ( AnyValue --> AnyValue )}

avec

Transform :: {$ :: Tableau de règles de réécriture [rw.r.] }

( [rw.r.] paires de fonctions prédicat et modèle)

par exemple, étant donné (... un autre exemple artificiel)

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

ensuite

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

rendements:

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

tandis que ... (très semblable <xsl:call-template name="betterGenderString">...)

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

rendements:

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

et

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

rendements:

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

4) Sinon

la fonction d'identité , dans tous les autres cas

c'est-à-dire Per :: T --> T

(c.-à-d. Per === function ( value ) { return value ; })

Remarque

dans (3) ci-dessus, le "this" d'un JavaScript dans le corps d'une fonction de modèle est donc lié à la transformation conteneur / propriétaire et à son ensemble de règles (tel que défini par le tableau $: [...]) - donc, faisant de l'expression "Per (this)", dans ce contexte, un équivalent fonctionnellement proche des XSLT

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

«HTH,

YSharp
la source
1
C'est plutôt cool.
Robert Harvey
@RobertHarvey: en plus de la complexité de la section 5.1 en soi que j'avais remarquée il y a longtemps, j'ai finalement été intrigué et inspiré par la remarque accrocheuse d'Evan Lenz "XSLT est plus simple que vous ne le pensez!", Sur http: // www. lenzconsulting.com/how-xslt-works - et j'ai donc décidé de tenter de vérifier cette affirmation (ne serait-ce que par curiosité) dans le langage très malléable qu'est JavaScript.
YSharp
Merci beaucoup pour votre réponse détaillée. Je suis occupé par d'autres choses (y compris mon propre équivalent XSLT), mais j'ai l'intention d'y revenir pour y jeter un œil plus attentif.
Brett Zamir
3

J'ai récemment créé une bibliothèque, json-transforms , exactement à cet effet:

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

Il utilise une combinaison de JSPath , une DSL sur le modèle de XPath et une approche de correspondance de modèle récursive, directement inspirée de XSLT.

Voici un petit exemple. Étant donné l'objet JSON suivant:

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

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

Qui a produit les éléments suivants:

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

Cette transformation est composée de trois règles. La première correspond à toute automobile fabriquée par Honda, émettant un objet avec une Hondapropriété, puis correspondant récursivement. La deuxième règle fait correspondre tout objet avec une makerpropriété, produisant les propriétés modelet year. La finale est la transformation d'identité qui correspond récursivement.

ColinE
la source
+1 et merci pour l'info. J'espère avoir mon propre github.com/brettz9/jtlt terminé à un moment donné, mais il est utile d'avoir plus d'implémentations à comparer.
Brett Zamir
-1

Je ne pense pas que vous obtiendrez jamais une variante JSON pour JSON en soi. Il existe plusieurs moteurs de modèles tels que Jinja2 de Python, Nunjucks JavaScripts, Groovy MarkupTemplateEngine et bien d'autres qui devraient être bien adaptés à ce que vous voulez. .NET prend en charge la sérialisation / désérialisation T4 et JSON, vous l'avez donc également.

Étant donné que les données JSON dersérialisées seraient essentiellement un dictionnaire ou une structure de carte, cela passerait simplement à votre moteur de modélisation et vous itéreriez sur les nœuds souhaités. Les données JSON sont ensuite transformées par le modèle.

greenaj
la source