Setara XSLT untuk JSON

14

Saya tertarik untuk menemukan (atau jika perlu mengembangkan) setara XSLT untuk JSON.

Karena saya belum menemukan satu pun, saya sedang mempertimbangkan bahasa query yang mungkin digunakan untuk mencocokkan jalur JSON sehingga dapat menerapkan templat (dari JavaScript) ketika ada kecocokan (mungkin hanya memeriksa serangkaian pola pencocokan secara berurutan, dan berhenti di template pertama yang cocok, meskipun memungkinkan untuk setara dengan xsl: apply-templat untuk membuat templat tetap berjalan untuk anak-anak).

Saya menyadari JSONPath, JSONQuery, dan RQL sebagai bahasa query JSON (meskipun saya tidak sepenuhnya jelas tentang apakah RQL mendukung jalur absolut dan relatif). Setiap saran tentang faktor untuk dipertimbangkan dan keuntungan relatif masing-masing terhadap penggunaan seperti itu.

Brett Zamir
sumber
Mungkin hanya pemikiran acak, JavaScript dan Kumis / Gagang? :)
Knerd
Terima kasih, tapi saya lebih tertarik menggunakan pendekatan standardish (misalnya, setidaknya satu dengan potensi, mengingat bahwa ekspresi jalur JSON generik akan menjadi rata-rata yang diakui secara umum merujuk JSON sebagai lawan beberapa sintaks khusus untuk perpustakaan).
Brett Zamir
1
Saya juga menemukan ini menarik: json-template.googlecode.com/svn/trunk/doc/…
Robert Harvey
Saya telah melakukan Json -> XML -> XSLT -> Json sebelumnya - itu berfungsi dengan baik, bahkan jika itu bukan solusi yang paling efisien,
user2813274

Jawaban:

27

XML: XSLT :: JSON: x . Apa itu x ?

Jawaban yang paling mudah adalah x = JavaScript. Meskipun Anda bisa mengajukan alasan untuk ini, rasanya tidak memuaskan. Meskipun XSLT secara teknis Turing lengkap , ada korespondensi yang buruk antara gaya deklaratif XSLT dan gaya yang lebih imperatif atau fungsional yang terlihat dalam JavaScript.

Ada beberapa bahasa permintaan JSON mandiri, seperti JSONPath , JSONiq , dan RQL yang mungkin berdiri di tengah XML: XPath :: JSON: y (atau mungkin, XQuery daripada XPath). Dan setiap database dokumen yang berfokus pada JSON memiliki bahasa query yang terkait dengan JSON .

Tetapi kenyataannya adalah, meskipun ada beberapa pesaing untuk posisi XSLT penuh, seperti SpahQL , tidak ada yang secara umum diterima, secara luas didukung setara JSON dengan XSLT.

Mengapa?

Dengan semua JSON di dunia, mengapa tidak ada analog (lebih langsung) ke XSLT? Karena banyak pengembang melihat XSLT sebagai percobaan yang gagal. Mesin pencari mana pun akan mengarah ke kutipan seperti "XSLT adalah kegagalan yang dibungkus dengan rasa sakit." Yang lain berpendapat bahwa jika itu hanya diformat lebih baik, itu akan lebih populer. Tetapi minat pada XSLT umumnya telah berkurang selama bertahun-tahun . Banyak alat yang mendukungnya hanya mendukung versi 1.0 , yang merupakan spesifikasi 1999. Spesifikasi berusia lima belas tahun? Ada spesifikasi 2.0 yang jauh lebih baru, dan jika orang antusias dengan XSLT, itu akan didukung. Bukan itu.

Pada umumnya pengembang telah memilih untuk memproses dan mengubah dokumen XML dengan kode, bukan template transformasi. Oleh karena itu tidak mengherankan bahwa ketika bekerja dengan JSON, mereka pada umumnya juga akan memilih untuk melakukannya dalam bahasa ibu mereka, daripada menambahkan sistem transformasi "asing" tambahan.

Jonathan Eunice
sumber
2
+1 karena ini adalah jawaban yang bijaksana, tapi saya masih berpikir bahwa lebih bersih untuk memiliki banyak template yang diatur secara linier dengan perpustakaan melakukan langkah-langkah, dan sementara saya pikir Anda mungkin benar tentang sikap terhadap XSL (saya akan bersandar ke kamp berpikir itu adalah masalah format meskipun gaya rekursif diakui perlu beberapa penyesuaian), saya yakin beberapa masalah mungkin inersia dalam perlu mengembangkan bahasa seperti itu untuk menggunakannya (misalnya, saya menemukan bahkan JSONPath sendiri membutuhkan beberapa peningkatan).
Brett Zamir
SpahQL tampaknya tidak memiliki templat sendiri, jadi sepertinya tidak ada pesaing yang benar-benar menggunakan JavaScript atau JSON murni untuk kode templat (bersama dengan struktur data) meskipun ada perpustakaan yang memungkinkan ekspresi HTML sebagai JSON / JS.
Brett Zamir
1
+1 terlepas dari kenyataan bahwa ada sesuatu tentang XSLT yang tidak dapat ditiru oleh cukup lainnya. JSON tentu saja akan menjadi sintaks yang lebih sulit untuk menulis yang setara dengan yang dapat digunakan.
user52889
7

Sementara Jonathan sebagian besar berbicara tentang sifat XSLT sebagai bahasa dalam jawabannya, saya pikir ada sudut pandang lain untuk dipertimbangkan.

Tujuan XSLT adalah untuk mengubah dokumen XML menjadi beberapa dokumen lain (XML, HTML, SGML, PDF, dll). Dengan cara ini, XSLT sering digunakan, secara efektif, sebagai bahasa templat.

Ada banyak sekali pustaka templat di luar sana, bahkan jika Anda membatasi diri Anda sendiri ke pustaka JavaScript (yang seharusnya tidak Anda perlukan, karena JS di JSON hanya merujuk pada asal notasi dan tidak boleh dianggap menyiratkan bahwa JSON hanya untuk JavaScript). Pemilih mesin template ini memberikan dan indikasi tentang berbagai opsi JS yang ada di luar sana.

Bagian terakhir dari pertanyaan Anda berbicara lebih banyak tentang bahasa permintaan dan versi XML ini adalah XPath (bukan XSLT). Seperti yang Anda perhatikan, ada berbagai opsi di sana dan saya tidak menambahkan apa pun ke daftar itu. Daerah ini relatif baru, jadi saya sarankan Anda memilih satu dan ikuti saja.

Dancrumb
sumber
Jika ada keraguan, saya pikir jawaban Jonathan luar biasa; Saya hanya ingin menambahkan perspektif alternatif.
Dancrumb
Ya, poin wajar (dan ya re: XPath menjadi setara untuk bagian kedua), tapi saya tertarik melihat JS XSL saya (menyebutnya JTLT) menggunakan JSONPath yang ditingkatkan untuk mengubah JSON ke bahasa lain juga (yaitu, HTML sebagai string atau DOM).
Brett Zamir
Saya memiliki perpustakaan saya sendiri bernama Jamilih yang saya sukai untuk mengekspresikan HTML mentah sebagai JS / JSON, tapi saya butuh sesuatu yang alami dan saya harap menarik untuk 1) Template dan pencocokan jalur 2) Iterating APIs setara dengan xsl: apply-templates dan xsl: call-template (xsl: untuk-masing-masing jelas untuk JS, tetapi tidak untuk JSON). Untuk JS, saya bisa menggunakan fungsi untuk templat, dan untuk JSON (berdasarkan Jamilih dan API pengulangan itu). Wills ee how it goes ...
Brett Zamir
3

Berikut adalah beberapa contoh apa yang dapat Anda lakukan dengan JSLT - JavaScript Lightweight Transforms (kecil [jslt.min.js] ) saya:

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

( [jslt.min.js] beratnya ~ 3,1kb yang diperkecil )

yaitu, hanya satu fungsi,

function Per ( subject ) { ... }

... yang sebenarnya meniru model pemrosesan XSLT (1.0) .

(lih fungsi dalam "transform" dan "templat", di tubuh Per)

Jadi, pada dasarnya, itu hanya semua dimasukkan ke dalam single function Per ( subject ) { ... }yang melakukan evaluasi pada jenis argumen uniknya, untuk diterapkan, baik:

1) Array subjek

pembuatan nodeset / filtering / perataan / pengelompokan / pemesanan / dll , jika subjek adalah array, di mana nodeset yang dihasilkan (juga Array ) diperluas dengan, dan terikat dengan metode yang sesuai ( hanya instance Array yang dikembalikan dari panggilan ke Per ( subjectArray )adalah diperpanjang; yaitu, Array.prototype dibiarkan tak tersentuh)

yaitu, Per :: Array --> Array

( metode ekstensi Array yang dihasilkan memiliki nama yang cukup jelas seperti, groupBy, orderBy, flattenBy, dll - lih. penggunaan dalam contoh-contoh)

2) Subjek string

interpolasi string , jika subjek adalah string

("Per" lalu mengembalikan objek dengan metode map ( source ), yang terikat ke string templat subjek )

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

misalnya,

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

hasil:

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

sementara salah satu dari

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

atau

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

menghasilkan yang sama:

"Those '0123456789' are our 10 digits."

tapi hanya

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

hasil panen

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

3) Ubah subjek

XSLT mirip-transformasi , jika subjek adalah hash dengan anggota "$" yang didefinisikan secara konvensional menyediakan array aturan penulisan ulang (dan sama seperti dalam (2), "Per" kemudian mengembalikan objek dengan metode yang map ( source )terikat pada subjek. mengubah - di mana

"ruleName" in Per ( subjectTransform [ , ruleName ])adalah opsional dan menyediakan fungsionalitas yang mirip dengan <xsl: call-template name = "templateName"> ...)

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

dengan

Transform :: {$ :: Array aturan penulisan ulang [rw.r.] }

( [rw.r.] pasangan fungsi predikat dan templat)

misalnya, diberikan (... contoh lain yang dibuat-buat)

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

kemudian

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

hasil:

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

sementara ... (mirip <xsl:call-template name="betterGenderString">...)

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

hasil:

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

dan

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

hasil:

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

4) Sebaliknya

fungsi identitas , dalam semua kasus lainnya

yaitu, Per :: T --> T

(yaitu, Per === function ( value ) { return value ; })

Catatan

pada (3) di atas, JavaScript "ini" di badan fungsi templat terikat dengan wadah / pemilik Transform dan seperangkat aturannya (sebagaimana didefinisikan oleh array $: [...]) - oleh karena itu, membuat ekspresi "Per (ini)", dalam konteks itu, setara fungsional-dekat dengan XSLT

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

'HTH,

YSharp
sumber
1
Itu keren sekali.
Robert Harvey
@RobertHarvey: selain kesederhanaan dari bagian 5.1 di dalam dan dari dirinya sendiri yang telah saya perhatikan sejak lama, saya akhirnya juga tertarik dan terinspirasi oleh komentar Evan Lenz yang menarik, "XSLT lebih sederhana daripada yang Anda pikirkan!", Di http: // www. lenzconsulting.com/how-xslt-works - dan jadi saya memutuskan untuk mencoba memverifikasi klaim itu (jika hanya ingin tahu) dalam bahasa yang sangat lunak yaitu JavaScript.
YSharp
Terima kasih banyak atas balasan terperinci Anda. Saya sibuk dengan beberapa hal lain (termasuk yang setara dengan XSLT saya), tetapi saya bermaksud untuk kembali ke sini untuk melihat lebih cermat.
Brett Zamir
3

Saya baru-baru ini membuat perpustakaan, json-transforms , tepatnya untuk tujuan ini:

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

Ia menggunakan kombinasi JSPath , DSL yang dimodelkan pada XPath, dan pendekatan pencocokan pola rekursif, yang terinspirasi langsung oleh XSLT.

Ini contoh singkatnya. Diberikan objek JSON berikut:

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

Inilah transformasi:

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

Yang menghasilkan sebagai berikut:

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

Transformasi ini terdiri dari tiga aturan. Yang pertama cocok dengan mobil apa pun yang dibuat oleh Honda, memancarkan objek dengan Hondaproperti, kemudian secara rekursif cocok. Aturan kedua cocok dengan objek apa pun dengan makerproperti, mengeluarkan modeldan yearproperti. Final adalah transformasi identitas yang cocok secara rekursif.

ColinE
sumber
+1 dan terima kasih atas informasinya. Saya berharap untuk mendapatkan github.com/brettz9/jtlt saya sendiri selesai pada beberapa titik, tetapi sangat membantu untuk memiliki lebih banyak implementasi untuk dibandingkan.
Brett Zamir
-1

Saya tidak berpikir Anda akan pernah mendapatkan varian JSON untuk JSON per se. Ada beberapa mesin templating seperti Python Jinja2, JavaScripts Nunjucks, Groovy MarkupTemplateEngine, dan banyak lainnya yang harus cocok untuk apa yang Anda inginkan. .NET memiliki dukungan serialisasi / deserialisasiisasi T4 dan JSON sehingga Anda juga memilikinya.

Karena data JSON yang derserialisasi pada dasarnya adalah sebuah kamus atau struktur peta, itu hanya akan melewati mesin templating Anda dan Anda akan beralih pada node yang diinginkan di sana. Data JSON kemudian ditransformasikan oleh templat.

greenaj
sumber