JSON的XSLT等效项


15

我对寻找(或必要时开发)JSON的XSLT等效项感兴趣。

由于没有发现任何内容,我正在考虑使用可能的查询语言来匹配JSON路径,以便在匹配时应用模板(来自JavaScript)(可能只是按顺序检查匹配模式的数组,然后在第一个匹配的模板,尽管允许使用xsl:apply-templates等效,以使模板适用于子级)。

我知道JSONPath,JSONQuery和RQL是JSON查询语言(尽管我还不太清楚RQL是否支持绝对路径和相对路径)。关于要考虑的因素和每种方法相对优势的任何建议。


只是一个随意的想法,JavaScript和Moustache / Handlebars可能吗?:)
Knerd 2014年

谢谢,但是我更热衷于使用一种标准化的方法(例如,至少有潜力,因为通用JSON路径表达式将是引用JSON的通用方法,而不是特定于某个库的语法)。
布雷特·扎米尔


1
我也发现了这个有趣的地方:json-template.googlecode.com/svn/trunk/doc/…–
罗伯特·哈维

我之前做过Json-> XML->
XSLT-> Json-

Answers:


27

XML:XSLT :: JSON:x。什么是x

最简单的答案是x = JavaScript。尽管您可以为此提供理由,但感觉并不令人满意。即使XSLT从技术上讲是图灵完整的,但XSLT 的声明式样式与JavaScript中的命令式或函数式样式之间的对应关系仍然很差。

有几种独立的JSON查询语言,例如JSONPathJSONiqRQL可能代表XML的中间立场:XPath :: JSON:y(或者可能是XQuery而不是XPath)。每个以JSON为重点的文档数据库都有一种与JSON相关的查询语言

但是现实是,尽管有一些争夺XSLT职位的竞争者,例如SpahQL,但是还没有普遍接受的,广泛支持的XSLT JSON等效项。

为什么?

对于世界上所有的JSON,为什么没有(更直接的)XSLT模拟?因为许多开发人员将XSLT视为失败的实验。任何搜索引擎都会引出诸如“ XSLT是一个痛苦缠身的失败”之类的引号。其他人则争辩说,如果只是更好地格式化,它将更受欢迎。但是,多年来,人们对XSLT的兴趣普遍有所减少。许多支持它的工具仅支持1.0版(这是1999年的规范)。十五岁的规格?有一个更新的2.0规范,如果人们对XSLT充满热情,它将得到支持。不是。

大体上,开发人员选择使用代码而不是转换模板来处理和转换XML文档。因此,毫不奇怪,当使用JSON时,他们通常也会选择以其母语进行操作,而不是添加其他“外部”转换系统。


2
+1,因为这是一个经过深思熟虑的答复,但我仍然认为,将一堆线性排列的模板与一个库一起逐步执行会更清洁,而我认为您可能对XSL的态度是正确的(我想倾向于营地,尽管递归样式确实需要某种习惯,但认为这是一种格式问题),我敢打赌,其中一些问题可能是需要开发这种语言以便使用它的惯性(例如,我正在寻找甚至JSONPath本身也需要一些增强)。
布雷特·扎米尔

SpahQL似乎没有自己的模板,因此,尽管有一些库允许将HTML表示为JSON /,但是似乎没有竞争者实际使用纯JavaScript或JSON作为模板代码(以及数据结构)。 JS。
布雷特·扎米尔

1
尽管存在XSLT方面的某些东西,但没有其他东西可以设法复制,但+1。JSON无疑将是一种很难编写等效的语法。
user52889

7

虽然Jonathan在回答中主要谈到了XSLT作为一种语言的本质,但我认为还需要考虑另一个角度。

XSLT的目的是将XML文档转换为其他文档(XML,HTML,SGML,PDF等)。这样,XSLT通常有效地用作模板语言。

即使您将自己限制在JavaScript库中,也可以使用各种各样的模板库(您不必这样做,因为JSON中的JS仅指代符号的起源,因此不应被认为暗示着JSON仅适用于JavaScript)。此模板引擎选择器给出并指示了各种JS选项。

您的问题的后半部分将更多地讨论查询语言,这些语言的XML版本将是XPath(不是XSLT)。正如您所指出的,那里有多种选择,我没有什么可添加到该列表中的。这是一个相对较新的领域,所以我建议您选择一个并随身携带。


如果有任何疑问,我认为乔纳森的回答很好。我只是想添加一个替代的观点。
Dancrumb 2014年

是的,很公平(是的,并且:re:XPath与第二部分等效),但是我有兴趣看到我的JS XSL(称为JTLT)也利用增强的JSONPath将JSON转换为另一种语言(即HTML作为字符串或DOM)。
布雷特·扎米尔

我有我自己的名为Jamilih的库,我喜欢将原始HTML表示为JS / JSON,但我需要一些自然的东西,希望对1)模板和路径匹配有所帮助2)迭代与xsl:apply-templates和xsl等效的API:调用模板(对于JS,xsl:for-each很明显,而对于JSON则不明显)。对于JS,我可以将函数用于模板和JSON(基于Jamilih和那些迭代API)。Wills ee它如何发展……
Brett Zamir 2014年

3

以下是您可以使用我的(小型[jslt.min.js])JSLT(JavaScript轻量级转换)执行的一些示例:

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

[jslt.min.js]晕死〜3.1kb缩小的

也就是说,只有一个功能,

function Per ( subject ) { ... }

...实际上模仿XSLT(1.0)的处理模型

(请参阅Per的正文中的“ transform”和“ template”内部函数)

因此,从本质上讲,它只是将所有内容烘焙到该单一代码中function Per ( subject ) { ... },从而将其评估基于其(也是)唯一参数的类型来实现:

1)数组主题

节点集创建/过滤/展平/分组/排序/等,如果subject是一个数组,则在其中扩展所得的结点集(以及Array),并绑定到相应命名的方法(仅对的返回的Array实例Per ( subjectArray )是扩展;即,Array.prototype保持不变)

即,Per :: Array --> Array

(得到的Array的扩展方法具有不言自明的名称,例如groupBy,orderBy,flattenBy等-参见示例中的用法)

2)字符串主题

字符串插值(如果subject是字符串)

(然后,“ Per”返回一个带有method的对象,该方法map ( source )绑定到主题模板字符串)

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

例如,

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

产量:

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

而任何一个

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

要么

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

产生相同的结果:

"Those '0123456789' are our 10 digits."

但只有

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

产量

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

3)转换主题

XSLT相似的转换,如果主题是具有常规定义的 “ $”成员的哈希,该成员提供重写规则数组(与(2)中的相同,则“ Per”然后返回带有map ( source )绑定到主题的方法的对象转换-在哪里

“ ruleName”输入Per ( subjectTransform [ , ruleName ])是可选的,并提供类似于<xsl:call-template name =“ templateName”> ...的功能)

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

转换 :: {$ :: 重写规则数组[rw.r.] }

[rw.r.]谓词和模板函数对)

例如,给定(...另一个人为的例子)

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

然后

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

产量:

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

而...(非常相似<xsl:call-template name="betterGenderString">...

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

产量:

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

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

产量:

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

4)否则

身份函数,在所有其他情况下

即,Per :: T --> T

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

注意

在上面的(3)中,模板函数主体中的JavaScript“ this”因此绑定到容器/所有者Transform及其规则集(由$:[...]数组定义)-因此,在这种情况下,使表达式“ Per(this)”在功能上与XSLT的等效

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

'HTH,


1
太酷了。
罗伯特·哈维

@RobertHarvey:除了我很早就注意到的5.1节本身的简洁性,我最终还受到Evan Lenz引人注目的“ XSLT比您想象的简单!”的吸引和启发。 lenzconsulting.com/how-xslt-works-因此,我决定尝试使用JavaScript这种非常具有延展性的语言来验证该声明(如果只是出于好奇)。
YSharp

非常感谢您的详细答复。我正在忙于其他一些事情(包括我自己的XSLT等效项),但是我确实打算回到此位置进行更仔细的研究。
Brett Zamir

3

我最近为此创建了一个库json-transforms

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

它结合了JSPath,在XPath上建模的DSL和直接受XSLT启发的递归模式匹配方法。

这是一个简单的例子。给定以下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 }
  ]
};

这是一个转换:

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

输出以下内容:

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

此转换由三个规则组成。第一个匹配本田制造的任何汽车,发射具有Honda属性的对象,然后递归匹配。第二条规则将任何具有maker属性的对象匹配,输出modelyear属性。最后是递归匹配的身份转换。


+1,感谢您提供信息。我希望可以在某个时候完成自己的github.com/brettz9/jtlt,但是比较更多的实现会有所帮助。
布雷特·扎米尔

-1

我认为您不会获得JSON本身的JSON变体。存在一些模板化引擎,例如Python的Jinja2,JavaScripts Nunjucks,Groovy MarkupTemplateEngine,以及许多其他模板引擎,它们应该非常适合您的需求。.NET具有T4和JSON序列化/反序列化支持,因此您也有。

由于将反序列化的JSON数据基本上是字典或地图结构,因此将其传递给模板引擎,然后在该模板上迭代所需的节点。然后,JSON数据将通过模板进行转换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.