Odpowiednik XSLT dla JSON

15

Byłem zainteresowany znalezieniem (lub w razie potrzeby opracowaniem) odpowiednika XSLT dla JSON.

Ponieważ nie znalazłem żadnego, zastanawiałem się nad możliwym językiem zapytań, który mógłby zostać użyty do dopasowania ścieżek JSON, aby zastosować szablony (z JavaScript), gdy było dopasowanie (prawdopodobnie po prostu sprawdzając tablicę pasujących wzorców w kolejności i zatrzymując się na pierwszy szablon, który pasuje, ale dopuszcza ekwiwalent xsl: Apply-templates, aby szablony działały dla dzieci).

Znam JSONPath, JSONQuery i RQL jako języki zapytań JSON (chociaż nie byłem w pełni pewien, czy RQL obsługuje ścieżki bezwzględne i względne). Wszelkie sugestie dotyczące czynników do rozważenia i względne zalety każdego z nich w stosunku do takiego zastosowania.

Brett Zamir
źródło
Tylko przypadkowa myśl, może JavaScript i Wąsy / Kierownica? :)
Knerd
Dzięki, ale chętniej używam standardowego podejścia (np. Przynajmniej jednego z potencjałem, biorąc pod uwagę, że ogólne wyrażenia ścieżki JSON byłyby ogólnie uznanym środkiem odwoływania się do JSON, w przeciwieństwie do niektórych składni specyficznych dla biblioteki).
Brett Zamir
1
Znalazłem również to interesujące: json-template.googlecode.com/svn/trunk/doc/…
Robert Harvey
Zrobiłem Json -> XML -> XSLT -> Json wcześniej - działa dobrze, nawet jeśli nie jest to najbardziej wydajne rozwiązanie,
user2813274

Odpowiedzi:

27

XML: XSLT :: JSON: x . Co to jest x ?

Najłatwiejszą odpowiedzią byłoby x = JavaScript. Chociaż możesz to uzasadnić, wydaje się to niezadowalające. Mimo że XSLT jest technicznie kompletny w Turingu , istnieje słaba zgodność między deklaratywnym stylem XSLT a bardziej imperatywnymi lub funkcjonalnymi stylami widocznymi w JavaScript.

Istnieje kilka niezależnych języków zapytań JSON, takich jak JSONPath , JSONiq i RQL, które mogą zastąpić XML: XPath :: JSON: y (ewentualnie XQuery zamiast XPath). I każda baza danych dokumentów skoncentrowana na JSON ma język zapytań związany z JSON .

Jednak w rzeczywistości, pomimo kilku kandydatów do pełnej pozycji XSLT, takich jak SpahQL , nie ma ogólnie akceptowanych, szeroko obsługiwanych odpowiedników JSON dla XSLT.

Dlaczego?

Z całym JSON na świecie, dlaczego nie ma (bardziej bezpośredniego) analogu do XSLT? Ponieważ wielu programistów uważa XSLT za nieudany eksperyment. Każda wyszukiwarka doprowadzi do cytatów takich jak „XSLT to porażka pełna bólu”. Inni twierdzą, że gdyby był po prostu lepiej sformatowany, byłby bardziej popularny. Jednak zainteresowanie XSLT ogólnie maleje z biegiem lat . Wiele narzędzi, które go obsługują, obsługuje tylko wersję 1.0 , która jest specyfikacją z 1999 roku. Piętnaście lat specyfikacji? Istnieje znacznie nowsza specyfikacja 2.0, a jeśli ludzie byliby entuzjastycznie nastawieni do XSLT, byłaby obsługiwana. To nie jest

Ogólnie rzecz biorąc, programiści zdecydowali się przetwarzać i przekształcać dokumenty XML za pomocą kodu, a nie szablonów transformacji. Nic więc dziwnego, że pracując z JSON-em, zdecydowaliby się to zrobić w swoim ojczystym języku, zamiast dodawać dodatkowy „obcy” system transformacji.

Jonathan Eunice
źródło
2
+1, ponieważ jest to przemyślana odpowiedź, ale nadal uważam, że lepiej jest mieć kilka liniowo ułożonych szablonów z biblioteką wykonującą krok po kroku, i chociaż myślę, że prawdopodobnie masz rację co do stosunku do XSL (chciałbym skłaniam się ku obozowi, myśląc, że jest to kwestia formatowania, chociaż styl rekurencyjny wymaga wprawdzie pewnego przyzwyczajenia), założę się, że niektóre z problemów mogą wynikać z konieczności opracowania takiego języka, aby go używać (np. znajduję nawet sama JSONPath potrzebuje kilku ulepszeń).
Brett Zamir
SpahQL nie wydawał się mieć własnych szablonów, więc nadal wydaje się, że nie ma konkurentów, którzy faktycznie używają czystego JavaScript lub JSON dla kodu szablonu (wraz ze strukturami danych), mimo że istnieją biblioteki, które pozwalają wyrażać HTML jako JSON / JS.
Brett Zamir
1
+1, mimo że w XSLT jest coś, czego nic więcej nie można powielić. JSON z pewnością będzie trudniejszą składnią do napisania użytecznego odpowiednika.
user52889,
7

Podczas gdy Jonathan w dużej mierze mówi o naturze XSLT jako języka w swojej odpowiedzi, myślę, że należy rozważyć inny aspekt.

Celem XSLT było przekształcenie dokumentów XML w inny dokument (XML, HTML, SGML, PDF itp.). W ten sposób XSLT jest często skutecznie wykorzystywany jako język szablonów.

Istnieje szeroka gama bibliotek szablonów, nawet jeśli ograniczysz się do bibliotek JavaScript (co nie powinno być konieczne, ponieważ JS w JSON odnosi się tylko do genezy notacji i nie należy zakładać, że JSON jest tylko dla JavaScript). Ten selektor szablonów daje i wskazuje różnorodność dostępnych opcji JS.

Druga połowa twoich pytań mówi więcej o językach zapytań, a ich wersją XML będzie XPath (nie XSLT). Jak zauważyłeś, istnieje wiele opcji i nie mam nic do dodania do tej listy. Ten obszar jest stosunkowo nowy, więc sugeruję, aby wybrać jeden i po prostu iść z nim.

Dancrumb
źródło
W razie wątpliwości uważam, że odpowiedź Jonathana jest świetna; Chciałem tylko dodać alternatywną perspektywę.
Dancrumb,
Tak, sprawiedliwe punkty (i tak, jeśli chodzi o: XPath będący odpowiednikiem drugiej części), ale jestem zainteresowany, aby moja JS XSL (nazywająca ją JTLT) wykorzystywała ulepszoną JSONPath przekształcającą JSON w inny język (tj. HTML jako string lub DOM).
Brett Zamir
Mam własną bibliotekę o nazwie Jamilih, którą preferuję do wyrażania surowego HTML jako JS / JSON, ale potrzebuję czegoś, co czuje się naturalnie i mam nadzieję, że wpadnie w ucho 1) Szablony i dopasowanie ścieżek 2) Iterujące interfejsy API równoważne do xsl: Apply-templates i xsl: call-template (xsl: for-each jest oczywisty dla JS, ale nie dla JSON). W przypadku JS mogłem używać funkcji dla szablonów i dla JSON (w oparciu o Jamilih i te iterujące interfejsy API). Wills ee jak to idzie ...
Brett Zamir
3

Oto kilka przykładów tego, co możesz zrobić z moim (małym [jslt.min.js] ) JSLT - JavaScript Lightweight Transforms:

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

( [jslt.min.js] waży ~ ok. 3,1kb )

to jest tylko jedna funkcja,

function Per ( subject ) { ... }

... który faktycznie naśladuje model przetwarzania XSLT (1.0) .

(por. funkcje wewnętrzne „transformacji” i „szablonu” w ciele Pera)

Zasadniczo jest to po prostu wszystko upieczone w tym singlu, function Per ( subject ) { ... }który wymaga oceny swojego (także) unikalnego argumentu, który można zaimplementować:

1) Tablica przedmiotem

tworzenie zestawów węzłów / filtrowanie / spłaszczanie / grupowanie / porządkowanie / itp. , jeśli podmiot jest tablicą, w której wynikowy zestaw węzłów (także tablica ) jest rozszerzany i powiązany z metodami odpowiednio nazwanymi ( tylko zwrócona instancja Array wywołania Per ( subjectArray )to rozszerzony, tj. Array.prototype pozostaje nietknięty)

tj. Per :: Array --> Array

(wynikowe metody rozszerzenia macierzy posiadające zrozumiałe nazwy, takie jak groupBy, orderBy, flattenBy itp. - por. użycie w przykładach)

2) Temat łańcucha

interpolacja łańcucha , jeśli temat jest łańcuchem

(„Per” następnie zwraca obiekt metodą map ( source ), która jest powiązana z ciągiem szablonu tematu )

tj. Per :: String --> {map :: ( AnyValue --> String )}

na przykład,

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

daje:

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

podczas gdy którykolwiek z

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

lub

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

daje to samo:

"Those '0123456789' are our 10 digits."

lecz tylko

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

daje

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

3) Przekształć temat

Transformacja podobna do XSLT , jeśli podmiot jest skrótem z konwencjonalnie zdefiniowanym elementem „$” zapewniającym tablicę reguł przepisywania (i tak samo jak w (2), „Per” następnie zwraca obiekt metodą map ( source )powiązaną z podmiotem przekształcać - gdzie

„nazwa_zasady” w Per ( subjectTransform [ , ruleName ])jest opcjonalna i zapewnia funkcjonalność podobną do <xsl: call-template name = „templateName”> ...)

tj. Per :: ( Przekształć [, nazwa_zasady :: Ciąg ]) -->{map :: ( AnyValue --> AnyValue )}

z

Przekształć :: {$ :: Tablica reguł przepisywania [rw.r.] }

( [rw.r.] pary predykatów i funkcji szablonów)

np. podany (... inny wymyślony przykład)

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

następnie

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

daje:

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

podczas gdy ... (podobnie <xsl:call-template name="betterGenderString">...)

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

daje:

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

i

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

daje:

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

4) W przeciwnym razie

funkcja tożsamości we wszystkich innych przypadkach

tj. Per :: T --> T

(tj. Per === function ( value ) { return value ; })

Uwaga

w (3) powyżej, JavaScript „to” w treści funkcji szablonu jest w ten sposób związany z transformatorem kontenera / właściciela i jego zestawem reguł (zgodnie z tablicą $: [...]) - dlatego też dzięki czemu wyrażenie „Per (this)” w tym kontekście jest funkcjonalnie blisko równoważne z XSLT

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

„HTH,

YSharp
źródło
1
To fajnie.
Robert Harvey
@RobertHarvey: poza zwięzłością samej sekcji 5.1 , którą zauważyłem dawno temu, w końcu zaintrygowałem się i zainspirowałem chwytliwą uwagą Evana Lenza „XSLT jest prostsze niż myślisz!” Na stronie http: // www. lenzconsulting.com/how-xslt-works - dlatego postanowiłem spróbować zweryfikować to roszczenie (choćby z ciekawości) w bardzo plastycznym języku, jakim jest JavaScript.
YSharp
Bardzo dziękuję za szczegółową odpowiedź. Jestem zajęty innymi rzeczami (w tym własnym odpowiednikiem XSLT), ale zamierzam wrócić do tego, aby przyjrzeć się dokładniej.
Brett Zamir
3

Niedawno stworzyłem bibliotekę, transformaty json , właśnie w tym celu:

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

Wykorzystuje kombinację JSPath , DSL wzorowany na XPath oraz rekurencyjne podejście do dopasowywania wzorców, zainspirowane bezpośrednio XSLT.

Oto szybki przykład. Biorąc pod uwagę następujący obiekt 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 }
  ]
};

Oto transformacja:

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

Które generują następujące:

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

Ta transformacja składa się z trzech zasad. Pierwszy pasuje do każdego samochodu wyprodukowanego przez Hondę, emitując obiekt z Hondawłaściwością, a następnie dopasowując rekurencyjnie. Druga reguła dopasowuje dowolny obiekt z makerwłaściwością, wyprowadzając właściwości modeli year. Ostatnim jest transformacja tożsamości, która rekurencyjnie pasuje.

ColinE
źródło
+1 i dziękuję za informację. Mam nadzieję, że w pewnym momencie skończę moją własną stronę github.com/brettz9/jtlt , ale warto mieć więcej implementacji do porównania.
Brett Zamir
-1

Nie sądzę, że kiedykolwiek dostaniesz wariant JSON dla JSON per se. Istnieje kilka silników szablonów, takich jak Python's Jinja2, JavaScripts Nunjucks, Groovy MarkupTemplateEngine i wiele innych, które powinny dobrze pasować do tego, czego chcesz. .NET ma obsługę serializacji / deserializacji JSON w wersji T4 i JSON, więc masz to również.

Ponieważ derserializowane dane JSON byłyby w zasadzie strukturą słownikową lub mapową, po prostu przeszłyby do twojego silnika szablonów i iterowałyby tam pożądane węzły. Dane JSON są następnie przekształcane przez szablon.

greenaj
źródło