XSLT-эквивалент для JSON

15

Я был заинтересован в поиске (или, если необходимо, разработке) XSLT-эквивалента для JSON.

Так как я не нашел ни одного, я рассматривал возможный язык запросов, который можно использовать для сопоставления путей JSON, чтобы применять шаблоны (из JavaScript) при наличии совпадения (возможно, просто проверять массив сопоставляемых шаблонов по порядку и останавливаться на первый шаблон, который соответствует, хотя допускает эквивалент xsl: apply-templates, чтобы шаблоны оставались для детей).

Мне известны JSONPath, JSONQuery и RQL как языки запросов JSON (хотя я не до конца понимал, поддерживает ли RQL абсолютные и относительные пути). Любые предложения о факторах, которые следует учитывать, и относительные преимущества каждого из них в отношении такого использования.

Бретт Замир
источник
Просто случайная мысль, JavaScript и Усы / Руль может быть? :)
Knerd
Спасибо, но я больше заинтересован в использовании стандартного подхода (например, по крайней мере, одного с потенциалом, учитывая, что обобщенные выражения пути JSON будут общепризнанным средством ссылки на JSON, а не на некоторый синтаксис, специфичный для библиотеки).
Бретт Замир
1
Я также нашел это интересным: json-template.googlecode.com/svn/trunk/doc/…
Роберт Харви
Я уже делал Json -> XML -> XSLT -> Json - он отлично работает, даже если это не самое эффективное решение,
user2813274

Ответы:

27

XML: XSLT :: JSON: x . Что такое х ?

Самый простой ответ был бы x = JavaScript. Хотя вы могли бы обосновать это, это кажется неудовлетворительным. Хотя XSLT технически завершен по Тьюрингу , существует слабое соответствие между декларативным стилем XSLT и более императивными или функциональными стилями, которые можно увидеть в JavaScript.

Существует несколько автономных языков запросов JSON, таких как JSONPath , JSONiq и RQL, которые могут заменить золотую середину XML: XPath :: JSON: y (или, возможно, XQuery, а не XPath). И каждая база данных документов, ориентированная на JSON, имеет язык запросов, связанный с JSON .

Но реальность такова, что, несмотря на наличие нескольких претендентов на полную позицию XSLT, таких как SpahQL , не существует общепринятых, широко поддерживаемых эквивалентов JSON для XSLT.

Почему?

При всем мире JSON, почему нет (более прямого) аналога XSLT? Потому что многие разработчики считают XSLT неудачным экспериментом. Любая поисковая система приведет к цитатам типа «XSLT - это неудача, обернутая болью». Другие утверждают, что если бы он был лучше отформатирован, он был бы более популярным. Но интерес к XSLT в целом уменьшился за эти годы . Многие инструменты, которые поддерживают его, поддерживают только версию 1.0 , которая является спецификацией 1999 года. Пятнадцать лет спецификации? Существует гораздо более новая спецификация 2.0, и если бы люди были в восторге от XSLT, она была бы поддержана. Это не так.

В целом разработчики решили обрабатывать и преобразовывать XML-документы с помощью кода, а не шаблонов преобразования. Поэтому неудивительно, что при работе с JSON они в общем и целом предпочитают делать это на своем родном языке, а не добавлять дополнительную «чужую» систему преобразования.

Джонатан Юнис
источник
2
+1, так как это вдумчивый ответ, но я все еще думаю, что было бы чётко иметь кучу линейно расположенных шаблонов с библиотекой, выполняющей пошаговое выполнение, и хотя я думаю, что вы, вероятно, правы относительно отношения к XSL (я бы склоняюсь к лагерю, думая, что это проблема форматирования, хотя рекурсивный стиль, по общему признанию, нуждается в некоторой адаптации), я бы поспорил, что некоторые проблемы могут заключаться в инерции необходимости разработки такого языка для его использования (например, я нахожу даже сам JSONPath нуждается в нескольких улучшениях).
Бретт Замир
У SpahQL, похоже, не было своих собственных шаблонов, поэтому все еще кажется, что нет претендентов, которые на самом деле используют чистый JavaScript или JSON для кода шаблона (вместе со структурами данных), хотя есть библиотеки, которые позволяют выражать HTML как JSON / JS.
Бретт Замир
1
+1, несмотря на то, что в XSLT есть что-то, что больше ничего не удается воспроизвести. JSON, безусловно, будет более сложным синтаксисом, чтобы написать пригодный для использования эквивалент.
user52889
7

Хотя в своем ответе Джонатан в основном говорит о природе XSLT как языка, я думаю, что есть еще один аспект, который следует рассмотреть.

Целью XSLT было преобразование документов XML в какой-то другой документ (XML, HTML, SGML, PDF и т. Д.). Таким образом, XSLT часто эффективно используется в качестве языка шаблонов.

Существует обширный массив библиотек шаблонов, даже если вы ограничиваете себя библиотеками JavaScript (которые вам не нужны, поскольку JS в JSON относится только к происхождению нотации и не должно подразумевать, что JSON только для JavaScript). Этот селектор шаблонного движка дает и указывает на разнообразие вариантов JS, которые существуют там.

Во второй половине ваших вопросов больше говорится о языках запросов, и их версия XML будет XPath (не XSLT). Как вы заметили, есть множество вариантов, и я ничего не могу добавить в этот список. Эта область относительно новая, поэтому я бы посоветовал вам выбрать одну и просто пойти с ней.

Dancrumb
источник
В случае каких-либо сомнений, я думаю, что ответ Джонатана великолепен; Я просто хотел добавить альтернативную перспективу.
Dancrumb
Да, это справедливо (и да, повторяю: XPath является эквивалентом для второй части), но мне интересно, чтобы мой JS XSL (называющий его JTLT) использовал расширенное преобразование JSONPath JSON и в другой язык (т. Е. HTML как строка или DOM).
Бретт Замир
У меня есть своя собственная библиотека под названием Jamilih, которую я предпочитаю для выражения необработанного HTML-кода в виде JS / JSON, но мне нужно что-то естественное, и я надеюсь, что бросается в глаза 1) Шаблоны и сопоставление путей 2) Итерирование API-интерфейсов, эквивалентных xsl: apply-templates и xsl: call-template (xsl: for-each очевиден для JS, но не для JSON). Для JS я мог бы использовать функции для шаблонов и для JSON (на основе Jamilih и тех итеративных API). Уиллс, как дела ...
Бретт Замир,
3

Вот несколько примеров того, что вы можете сделать с моим (small [jslt.min.js] ) JSLT - JavaScript Lightweight Transforms:

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

( [jslt.min.js] весит ~ 3,1 кб минимизировано )

то есть только одна функция,

function Per ( subject ) { ... }

... которая фактически имитирует модель обработки XSLT (1.0) .

(см. внутренние функции "transform" и "template" в теле Пера)

Так что, по сути, это просто все, что встроено в тот единственный, function Per ( subject ) { ... }который выполняет оценку по типу своего (также) уникального аргумента, чтобы реализовать либо:

1) Массив субъект

Создание набора узлов / фильтрация / уплощение / Группировка / упорядочение / и т.д. , если объект является массив, в котором результирующий набор узлы (ый массив , а) продолжаются с, и связаны с методами , названных соответствующим образом ( только возвращаемым массивом экземпляром вызова Per ( subjectArray )IS расширенный, т. е. Array.prototype остается нетронутым)

то есть, Per :: Array --> Array

(результирующие методы расширения Array , имеющие понятные имена, такие как groupBy, orderBy, flattenBy и т. д. - см. использование в примерах)

2) Строковый предмет

интерполяция строки , если тема является строкой

(«Per» затем возвращает объект с методом 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 )}

с

Transform :: {$ :: Массив правил переписывания [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="..."/>

«НТН,

YSharp
источник
1
Это круто.
Роберт Харви
@RobertHarvey: помимо краткости самого раздела 5.1 , который я уже давно заметил, я в конечном итоге также был заинтригован и вдохновлен броским замечанием Эвана Ленца «XSLT проще, чем вы думаете!» На сайте http: // www. lenzconsulting.com/how-xslt-works - и поэтому я решил попытаться проверить это утверждение (хотя бы из любопытства) на очень податливом языке, который является JavaScript.
YSharp
Большое спасибо за ваш подробный ответ. Я занят другими вещами (включая мой собственный XSLT-эквивалент), но я намерен вернуться к этому, чтобы более внимательно посмотреть.
Бретт Замир
3

Я недавно создал библиотеку, json-transforms , именно для этой цели:

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

Он использует комбинацию JSPath , DSL, смоделированного на XPath, и рекурсивный подход сопоставления с образцом, вдохновленный непосредственно 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, испускает объект со Hondaсвойством, затем рекурсивно сопоставляется. Второе правило соответствует любому объекту с makerсобственности, выдачи сигнала modelи yearсвойства. Финальным является преобразование идентичности, которое рекурсивно совпадает.

Coline
источник
+1 и спасибо за информацию. Я надеюсь в какой-то момент завершить свой собственный github.com/brettz9/jtlt , но полезно иметь больше реализаций для сравнения.
Бретт Замир
-1

Я не думаю, что вы когда-нибудь получите вариант JSON для JSON как таковой. Существует несколько шаблонизаторов, таких как Python Jinja2, JavaScripts Nunjucks, Groovy MarkupTemplateEngine и многие другие, которые должны хорошо подходить для того, что вы хотите. .NET имеет поддержку сериализации / десериализации T4 и JSON, так что у вас есть это.

Поскольку дерисериализированные данные JSON будут в основном словарем или структурой карты, они просто перейдут к вашему шаблонизатору, и вы будете перебирать нужные узлы там. Затем данные JSON преобразуются шаблоном.

greenaj
источник