XSLT equivalente para JSON

14

Eu estava interessado em encontrar (ou, se necessário, desenvolver) um equivalente XSLT para JSON.

Como não encontrei nenhuma, estava considerando a possível linguagem de consulta a ser usada para caminhos JSON correspondentes, de modo a aplicar modelos (do JavaScript) quando houve uma correspondência (provavelmente apenas verificando uma matriz de padrões correspondentes em ordem e parando no primeiro modelo que corresponda, embora permitindo o equivalente a xsl: apply-templates para manter os modelos ativos para filhos).

Estou ciente do JSONPath, JSONQuery e RQL como linguagens de consulta JSON (embora não tenha sido totalmente claro se o RQL suporta caminhos absolutos e relativos). Quaisquer sugestões sobre fatores a serem considerados e vantagens relativas de cada um em relação a esse uso.

Brett Zamir
fonte
Apenas um pensamento aleatório, JavaScript e Bigode / Guidão, talvez? :)
Knerd
Obrigado, mas estou mais interessado em usar uma abordagem padronizada (por exemplo, pelo menos uma com potencial, já que expressões de caminho JSON genéricas seriam um meio genericamente reconhecido de referenciar JSON em vez de alguma sintaxe específica de uma biblioteca).
Brett Zamir
1
Eu também achei isso interessante: json-template.googlecode.com/svn/trunk/doc/…
Robert Harvey
Eu tenho feito Json -> XML -> XSLT -> Json antes - ele funciona muito bem, mesmo se não é a solução mais eficiente,
user2813274

Respostas:

27

XML: XSLT :: JSON: x . O que é x ?

A resposta mais fácil seria x = JavaScript. Embora você possa defender isso, parece insatisfatório. Embora o XSLT seja tecnicamente completo em Turing , há uma correspondência fraca entre o estilo declarativo do XSLT e os estilos mais imperativos ou funcionais vistos no JavaScript.

Existem algumas linguagens de consulta JSON independentes, como JSONPath , JSONiq e RQL, que podem representar o meio termo do XML: XPath :: JSON: y (ou possivelmente XQuery em vez de XPath). E todo banco de dados de documentos focado em JSON tem uma linguagem de consulta relacionada a JSON .

Mas a realidade é que, apesar de haver alguns candidatos à posição XSLT completa, como o SpahQL , não há equivalentes JSON geralmente aceitos e amplamente suportados pelo XSLT.

Por quê?

Com todo o JSON do mundo, por que não existe um analógico (mais direto) para o XSLT? Porque muitos desenvolvedores veem o XSLT como um experimento com falha. Qualquer mecanismo de pesquisa levará a aspas como "XSLT é uma falha envolvida na dor". Outros argumentaram que, se fosse apenas melhor formatado, seria mais popular. Mas o interesse no XSLT geralmente diminuiu ao longo dos anos . Muitas ferramentas que o suportam suportam apenas a versão 1.0 , que é uma especificação de 1999. Especificações de quinze anos? Há uma especificação 2.0 muito mais nova e, se as pessoas estiverem entusiasmadas com o XSLT, elas serão suportadas. Não é.

Em geral, os desenvolvedores optaram por processar e transformar documentos XML com código, não modelos de transformação. Portanto, não surpreende que, ao trabalhar com o JSON, eles também optem por fazê-lo em seu idioma nativo, em vez de adicionar um sistema de transformação "estrangeiro" adicional.

Jonathan Eunice
fonte
2
+1, pois esta é uma resposta ponderada, mas ainda acho que é mais fácil ter um monte de modelos organizados linearmente com uma biblioteca fazendo o passo a passo. Embora eu ache que você provavelmente esteja certo sobre a atitude em relação ao XSL (eu inclinado para o acampamento pensando que é uma questão de formatação, embora o estilo recursivo precise de alguma personalização), aposto que parte do problema pode ser inércia na necessidade de desenvolver uma linguagem para usá-la (por exemplo, estou descobrindo até o próprio JSONPath precisa de alguns aprimoramentos).
Brett Zamir
O SpahQL não parecia ter seus próprios modelos, então ainda parece que não há concorrentes que realmente usem JavaScript ou JSON puro para o código do modelo (junto com as estruturas de dados), embora existam bibliotecas que permitem a expressão de HTML como JSON / JS.
Brett Zamir
1
+1, apesar do fato de haver algo sobre o XSLT que nada mais consegue replicar. O JSON certamente será uma sintaxe mais difícil de se escrever um equivalente utilizável.
precisa saber é o seguinte
7

Enquanto Jonathan fala amplamente sobre a natureza do XSLT como uma linguagem em sua resposta, acho que há outro ângulo a considerar.

O objetivo do XSLT era transformar documentos XML em outro documento (XML, HTML, SGML, PDF, etc). Dessa maneira, o XSLT é freqüentemente usado, efetivamente, como uma linguagem de modelo.

Existe uma vasta gama de bibliotecas de modelos por aí, mesmo se você se restringir às bibliotecas JavaScript (o que não é necessário, pois o JS no JSON se refere apenas à gênese da notação e não deve ser considerado que o JSON é apenas para JavaScript). Esse seletor de mecanismo de modelo fornece e indica a variedade de opções de JS existentes.

A segunda metade de suas perguntas fala mais sobre linguagens de consulta e a versão XML delas seria XPath (não XSLT). Como você observou, há uma variedade de opções e eu não tenho nada a acrescentar a essa lista. Esta área é relativamente nova, então eu sugiro que você escolha uma e continue.

Dancrumb
fonte
Caso haja alguma dúvida, acho a resposta de Jonathan ótima; Eu só queria adicionar uma perspectiva alternativa.
Dancrumb
Sim, pontos justos (e sim re: XPath é o equivalente para a segunda parte), mas estou interessado em ver meu JS XSL (chamando-o de JTLT) utilizar uma JSONPath aprimorada para transformar JSON em outra linguagem também (por exemplo, HTML como um string ou DOM).
Brett Zamir
Eu tenho minha própria biblioteca chamada Jamilih, que sou a favor de expressar HTML bruto como JS / JSON, mas preciso de algo natural e espero que cativante 1) Modelos e caminho correspondente 2) APIs de iteração equivalentes a xsl: apply-templates e xsl: modelo de chamada (xsl: for-each é óbvio para JS, mas não JSON). Para JS, eu poderia usar funções para modelos e para JSON (com base no Jamilih e nas APIs de iteração). Wills ee como ele vai ...
Brett Zamir
3

Aqui estão alguns exemplos do que você pode fazer com o meu (small [jslt.min.js] ) JSLT - JavaScript Lightweight Transforms:

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

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

isto é, apenas uma função,

function Per ( subject ) { ... }

... que realmente imita o modelo de processamento do XSLT (1.0) .

(cf. as funções internas "transformar" e "modelo", no corpo de Per)

Portanto, em essência, é simplesmente tudo incluído naquele single function Per ( subject ) { ... }que bifurca sua avaliação no tipo de seu (também) argumento único, para implementar:

1) Assunto da matriz

criação / filtragem de conjunto de nós / filtragem / nivelamento / agrupamento / pedido / etc , se o assunto for uma matriz, em que o conjunto de nós resultante (também uma matriz ) é estendido e vinculado a métodos nomeados de acordo ( apenas a instância de matriz retornada da chamada para Per ( subjectArray )é estendido; ou seja, Array.prototype permanece intocado)

ou seja, Per :: Array --> Array

(os métodos de extensão da matriz resultantes com nomes auto-explicativos, como groupBy, orderBy, flattenBy, etc. - consulte o uso nos exemplos)

2) Assunto da string

interpolação de string , se o assunto for uma string

("Per" retorna um objeto com um método map ( source ), que é vinculado à sequência do modelo do assunto )

ou seja, Per :: String --> {map :: ( AnyValue --> String )}

por exemplo,

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

rendimentos:

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

enquanto qualquer um

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)

produz o mesmo:

"Those '0123456789' are our 10 digits."

se apenas

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

rendimentos

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

3) Transformar assunto

Transformação semelhante XSLT , se o assunto for um hash com um membro "$" definido convencionalmente, fornecendo a matriz de regras de reescrita (e mesmo que em (2), "Per" retornará um objeto com um método map ( source )vinculado ao assunto transformar - onde

"ruleName" in Per ( subjectTransform [ , ruleName ])é opcional e fornece funcionalidade semelhante a <xsl: call-template name = "templateName"> ...)

ou seja, Por :: ( Transform [, ruleName :: String ]) -->{map :: ( AnyValue --> AnyValue )}

com

Transform :: {$ :: Matriz de regras de reescrita [rw.r.] }

( [rw.r.] pares de funções de predicado e modelo)

por exemplo, dado (... outro exemplo artificial)

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

então

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

rendimentos:

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

enquanto ... (muito parecido <xsl:call-template name="betterGenderString">...)

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

rendimentos:

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

e

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

rendimentos:

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

4) Caso contrário

a função de identidade , em todos os outros casos

ou seja, Per :: T --> T

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

Nota

em (3) acima, o "this" de um JavaScript no corpo de uma função de template está vinculado à transformação container / owner e seu conjunto de regras (conforme definido pela matriz $: [...]) - portanto, tornando a expressão "Per (this)", nesse contexto, um equivalente funcionalmente próximo aos XSLT's

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

«HTH,

YSharp
fonte
1
Isso é bem legal.
Robert Harvey
@RobertHarvey: além da discrepância da seção 5.1 em si mesma que eu havia notado há muito tempo, eu também fiquei intrigado e inspirado pela cativante observação de Evan Lenz "XSLT é mais simples do que você pensa!", Em http: // www. lenzconsulting.com/how-xslt-works - e, por isso, decidi tentar verificar essa afirmação (mesmo que por curiosidade) na linguagem muito maleável que é o JavaScript.
precisa saber é
Muito obrigado pela sua resposta detalhada. Estou ocupado com algumas outras coisas (incluindo meu próprio equivalente XSLT), mas pretendo voltar a isso para dar uma olhada mais cuidadosa.
Brett Zamir
3

Eu criei recentemente uma biblioteca, json-transforma , exatamente para esse fim:

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

Ele usa uma combinação de JSPath , uma DSL modelada no XPath e uma abordagem de correspondência de padrão recursiva, inspirada diretamente no XSLT.

Aqui está um exemplo rápido. Dado o seguinte objeto 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 }
  ]
};

Aqui está uma transformação:

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

Que produz o seguinte:

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

Essa transformação é composta por três regras. O primeiro corresponde a qualquer automóvel fabricado pela Honda, emitindo um objeto com uma Hondapropriedade e depois correspondendo recursivamente. A segunda regra corresponde a qualquer objeto com uma makerpropriedade, gerando as propriedades modele year. A final é a transformação de identidade que corresponde recursivamente.

ColinE
fonte
+1 e obrigado pela informação. Espero concluir meu próprio github.com/brettz9/jtlt em algum momento, mas é útil ter mais implementações para comparar.
Brett Zamir
-1

Eu acho que você nunca receberá uma variante JSON para JSON por si só. Existem vários mecanismos de modelagem, como o Jinja2 do Python, o JavaScripts Nunjucks, o Groovy MarkupTemplateEngine e muitos outros que devem ser adequados ao que você deseja. O .NET tem suporte para serialização / desserialização T4 e JSON, então você também tem isso.

Como os dados JSON derserializados seriam basicamente uma estrutura de dicionário ou mapa, isso passaria apenas para o mecanismo de modelo e você iteraria nos nós desejados lá. Os dados JSON são transformados pelo modelo.

greenaj
fonte