XSLT equivalent for JSON

I was interested in finding (or if necessary developing) an XSLT equivalent for JSON.

As I have not found any, I was considering the possible query language to use for matching JSON paths so as to apply templates (from JavaScript) when there was a match (probably just checking an array of matching patterns in order, and stopping at the first template which matches, though allowing for the equivalent of xsl:apply-templates to keep templates going for children).

I am aware of JSONPath, JSONQuery, and RQL as JSON query languages (though I was not fully clear on whether RQL supported absolute and relative paths). Any suggestions on factors to consider and relative advantages of each toward such a usage.

7

XML : XSLT :: JSON : x. What is x ?

The most facile answer would be x = JavaScript. Though you could make a case for this, it feels unsatisfying. Even though XSLT is technically Turing complete, there is a poor correspondence between the declarative style of XSLT and the more imperative or functional styles seen in JavaScript.

There are a few standalone JSON query languages, like JSONPath, JSONiq, and RQL which might stand in for the middle ground of XML : XPath :: JSON : y (or possibly, XQuery rather than XPath). And every JSON-focused document database has a JSON-related query language.

But the reality is, despite there being a few contenders for the full XSLT position, such as SpahQL, there are no generally accepted, broadly supported JSON equivalents to XSLT.

Why?

With all the JSON in the world, why isn’t there a (more direct) analog to XSLT? Because many developers view XSLT as a failed experiment. Any search engine will lead to to quotes like “XSLT is a failure wrapped in pain.” Others have argued that if it were just better formatted, it would be more popular. But interest in XSLT has generally diminished over the years. Many tools that support it support only version 1.0, which is a 1999 specification. Fifteen year old specs? There is a much newer 2.0 spec, and if people were enthusiastic about XSLT, it would be supported. It isn’t.

By and large developers have chosen to process and transform XML documents with code, not transformation templates. It is therefore unsurprising that when working with JSON, they would also by and large choose to do that in their native language, rather than adding an additional “foreign” transformation system.

3

While Jonathan largely talks about the nature of XSLT as a language in his answer, I think there’s another angle to consider.

The purpose of XSLT was to transform XML documents into some other document (XML, HTML, SGML, PDF, etc). In this way, XSLT is frequently used, effectively, as a template language.

There is a vast array of template libraries out there, even if you restrict yourself to JavaScript libraries (which you shouldn’t need to, since the JS in JSON only refers to the genesis of the notation and should not be taken to imply that JSON is only for JavaScript). This template engine selector gives and indication of the variety of JS options there are out there.

The latter half of your questions talks more about query languages and the XML version of these would be XPath (not XSLT). As you noted, there are a variety of options there and I don’t anything to add to that list. This area is relatively new, so I’d suggest you pick one and just go with it.

3

I recently created a library, json-transforms, exactly for this purpose:

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

It uses a combination of JSPath, a DSL modelled on XPath, and a recursive pattern matching approach, inspired directly by XSLT.

Here’s a quick example. Given the following JSON object:

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

Here’s a 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);

Which output the following:

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

This transform is composed of three rules. The first matches any automobile which is made by Honda, emitting an object with a Honda property, then recursively matching. The second rule matches any object with a maker property, outputting the model and year properties. The final is the identity transform that recursively matches.

1

Here are a few examples of what you can do with my (small[jslt.min.js]) JSLT — JavaScript Lightweight Transforms:

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

([jslt.min.js] weighs ~ 3.1kb minified)

that is, just one function,

function Per ( subject ) { ... }

… which actually mimics XSLT (1.0)’s processing model.

(cf. the “transform” and “template” inner functions, in Per’s body)

So, in essence, it’s simply just all baked into that single function Per ( subject ) { ... } which forks its evaluation on the type of its (also) unique argument, to implement, either:

1) Array subject

nodeset creation / filtering / flattening / grouping / ordering / etc, if subject is an array, where the resulting nodeset (an Array as well) is extended with, and bound to methods named accordingly (only the returned Array instance of the call to Per ( subjectArray ) is extended; i.e., Array.prototype is left untouched)

i.e., Per :: Array --> Array

(the resulting Array‘s extension methods having self-explanatory names such as, groupBy, orderBy, flattenBy, etc — cf. the usage in the examples)

2) String subject

string interpolation, if subject is a string

(“Per” then returns an object with a method map ( source ), which is bound to the subject template string)

i.e., Per :: String --> { map :: (AnyValue --> String) }

e.g.,

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

yields:

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

while either of

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

or

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

yields the same:

"Those '0123456789' are our 10 digits."

but only

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

yields

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

3) Transform subject

XSLT look-alike transformation, if the subject is a hash with a conventionally-defined “$” member providing the array of rewriting rules (and same as in (2), “Per” then returns an object with a method map ( source ) bound to the subject transform — where

“ruleName” in Per ( subjectTransform [ , ruleName ]) is optional and provides functionality similar to <xsl:call-template name=”templateName”>…)

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

with

Transform :: { $ :: Array of rewriting rules[rw.r.] }

([rw.r.] predicate and template function pairs)

e.g., given (… another contrived example)

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

then

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

yields:

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

while… (much alike <xsl:call-template name="betterGenderString">...)

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

yields:

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

and

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

yields:

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

4) Otherwise

the identity function, in all other cases

i.e., Per :: T --> T

( i.e., Per === function ( value ) { return value ; } )

Note

in (3) above, a JavaScript’s “this” in the body of a template function is thus bound to the container / owner Transform and its set of rules (as defined by the $: [ … ] array) — therefore, making the expression “Per(this)”, in that context, a functionally-close equivalent to XSLT’s

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

‘HTH,

3

I don’t think you will ever get an JSON variant for JSON per se. There exist several templating engines such as Python’s Jinja2, JavaScripts Nunjucks, the Groovy MarkupTemplateEngine, and many others that should be well suited for what you want. .NET has T4 and JSON serialization/deserialization support so you have that also.

Since the derserialized JSON data would be basically a dictionary or map structure, that would just get past to your templating engine and you would iterate over the desired nodes there. The JSON data then gets transformed by the template.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật