XSLT equivalent for JSON [closed]
Asked Answered
J

25

522

Is there an XSLT equivalent for JSON? Something to allow me to do transformations on JSON like XSLT does to XML.

Jadda answered 24/10, 2009 at 13:40 Comment(15)
Btw, which language/platform would this be on?Theoretics
@Theoretics XSLT is a standard that has actual implementations in many languages and platforms, my questions targets a similar endeavor.Jadda
Yes, but while there doesn't seem to be exact equivalent, there might be applicable tools for subset of languages which could help.Theoretics
+1 for your question. Many people seem to overlook or plain dislike XSLT, but it may be simply a reaction to it XML verbosity. And indeed, as XML gradually falls out of favor, there are less and less opportunities to use XSLT, which is a pity! An XSLT equivalent for JSON would be awesome.Loreenlorelei
@NicolasLeThierryd'Ennequin Agreed. Lots of people hate XML, and therefore dismiss XSLT. The XML ecosystem of tools is also heavy on Java devs, which turns away even more people. But I was heavy into XSLT in the mid 2000s, and there is tremendous power that has no direct equivalent outside the XML ecosystem. I would love a JSON equivalent!Hazaki
However, part of what made XSLT awesome was other XML-related standards that it was built on. In particular: (1) the ability to validate an XML document with a schema (not required, but often used), and (2) XPath, my favorite ever language for navigating data hierarchies. Any solid XSLT equivalents for JSON would do well to consider these things as well.Hazaki
@NicolasLeThierryd'Ennequin : see jsfiddle.net/YSharpLanguage/kj9pk8oz/10 or jsfiddle.net/YSharpLanguage/ppfmmu15/10 or jsfiddle.net/YSharpLanguage/hvo24hmk/3Akanke
@Zearin: OP could reasonably expect anyone in a position to answer this question to be aware that an XSLT analog would need to be built on an XPath analog (though the XPath analog would not necessarily have a name of its own the way XPath does).Anatola
@BhargavRao I agree this should be closed, but I think the Recommendation reason would be better, since it's asking for us to name some standard or tool that may or may not exist rather than for code.Wavell
ANSWER: Yes, there is: See JSON Patch. It is a proposed standard (RFC 6902).Sullivan
For C#/.NET there is a library that can do JSON transforms "JUST - JSON Under Simple Transformation (XSLT equivalent for JSON)" github.com/WorkMaze/JUST.netEtiquette
link please try to use convert xslt to jsonAhoufe
I strongly disagree with the closing of this answer. A simple rephrasing would have sufficed: "How does one restructure JSON into JSON declaratively?"Cazares
This is closed-ended and objective enough of a question that I think we can reopen it. Technically it is asking for a tool, but it is of a completely different nature than the typical question that gets closed for that type of reason.Norford
Why not JSON -convert-> XML -XSLT-> XML -convert-> JSON? Converting from JSON to XML is just a choice how to convert one structured format into some other strutured format, then apply XSLT and finally you have to convert back. As you chose how the XML looks like after the conversion to XML, converting back should be straight forward in that case (there probably is no good way to create some "any XML to JSON" converter, as there are too many ways to express something in XML, while JSON is fairly settled in how things are serialized).Amalbergas
M
79

Interesting idea. Some searching on Google produced a few pages of interest, including:

Hope this helps.

Mohawk answered 24/10, 2009 at 17:59 Comment(3)
Yup, thank you, that's what I was looking for. It's a pity the technique isn't more popular, JSON is quite often used as a return format in REST-style services and it would be nice to have a standard way of implementing transformations to it.Jadda
Link only answerEventful
This answer seems irrelevant. Best I can tell, the link above saying "a company may have implemented something suitable" ultimately leads to nothing. An additional search for XJR (the purported product) is fruitless.Survive
T
214

XSLT equivalents for JSON - a list of candidates (tools and specs)

Tools

1. XSLT

You can use XSLT for JSON with the aim of fn:json-to-xml.

This section describes facilities allowing JSON data to be processed using XSLT.

2. jq

jq is like sed for JSON data - you can use it to slice and filter and map and transform structured data with the same ease that sed, awk, grep and friends let you play with text. There are install packages for different OS.

3. jj

JJ is a command line utility that provides a fast and simple way to retrieve or update values from JSON documents. It's powered by GJSON and SJSON under the hood.

4. fx

Command-line JSON processing tool

  • Don't need to learn new syntax
  • Plain JavaScript
  • Formatting and highlighting
  • Standalone binary

5. CsvCruncher

Command-line tabular data SQL-based processing tool

  • Don't need to learn new syntax
  • Suitable when JSON contains a large array with similar items
  • Supports multiple documents to be processed as multiple SQL tables
  • Written in Kotlin, runs on Java
  • Also available as a Java library in Maven Central repository

6. jl

jl ("JSON lambda") is a tiny functional language for querying and manipulating JSON.

7. JOLT

JSON to JSON transformation library written in Java where the "specification" for the transform is itself a JSON document.

8. gron

Make JSON greppable! gron transforms JSON into discrete assignments to make it easier to grep for what you want and see the absolute 'path' to it. It eases the exploration of APIs that return large blobs of JSON but have terrible documentation.

9. json-e

JSON-e is a data-structure parameterization system for embedding context in JSON objects. The central idea is to treat a data structure as a "template" and transform it, using another data structure as context, to produce an output data structure.

10. JSLT

JSLT is a complete query and transformation language for JSON. The language design is inspired by jq, XPath, and XQuery.

11. JSONata

JSONata is a lightweight query and transformation language for JSON data. Inspired by the 'location path' semantics of XPath 3.1, it allows sophisticated queries to be expressed in a compact and intuitive notation.

12. JSONPath Plus

Analyse, transform, and selectively extract data from JSON documents (and JavaScript objects). jsonpath-plus expands on the original specification to add some additional operators and makes explicit some behaviors the original did not spell out.

13. json-transforms Last Commit Dec 1, 2017

Provides a recursive, pattern-matching approach to transforming JSON data. Transformations are defined as a set of rules which match the structure of a JSON object. When a match occurs, the rule emits the transformed data, optionally recursing to transform child objects.

14. json Last commit Jun 23, 2018

json is a fast CLI tool for working with JSON. It is a single-file node.js script with no external deps (other than node.js itself).

15. jsawk Last commit Mar 4, 2015

Jsawk is like awk, but for JSON. You work with an array of JSON objects read from stdin, filter them using JavaScript to produce a results array that is printed to stdout.

16. yate Last Commit Mar 13, 2017

Tests can be used as docu https://github.com/pasaran/yate/tree/master/tests

17. jsonpath-object-transform Last Commit Jan 18, 2017

Pulls data from an object literal using JSONPath and generate a new objects based on a template.

18. Stapling Last Commit Sep 16, 2013

Stapling is a JavaScript library that enables XSLT formatting for JSON objects. Instead of using a JavaScript templating engine and text/html templates, Stapling gives you the opportunity to use XSLT templates - loaded asynchronously with Ajax and then cached client side - to parse your JSON datasources.

19. mapneat

MapNeat is a JVM library written in Kotlin that provides an easy to use DSL (Domain Specific Language) for transforming JSON to JSON, XML to JSON, POJO to JSON in a declarative way.

Specs:

JSON Pointer is a string syntax or address to identify a specific object within a larger JSON object. It does not have querying features or transformation functions. The referent of a JSON Pointer can be any JSON object.

JSONPath expressions always refer to a JSON structure in the same way as XPath expression are used in combination with an XML document

JSPath for JSON is like XPath for XML."

The main source of inspiration behind JSONiq is XQuery, which has been proven so far a successful and productive query language for semi-structured data

JMESPath is a query language for JSON. The JMESPath language is described in an ABNF grammar with a complete specification.

Theme answered 27/2, 2018 at 14:49 Comment(3)
Thank you for your very detailled and usefull post. In order to transform one-line json to a readable form, jq (nr.2 in your list) is for me the best choice. Thanks again!Ariosto
I often use json_pp for pretty printing. It is available for many distros.Theme
See also: under Specs: JMESpathAuricle
M
79

Interesting idea. Some searching on Google produced a few pages of interest, including:

Hope this helps.

Mohawk answered 24/10, 2009 at 17:59 Comment(3)
Yup, thank you, that's what I was looking for. It's a pity the technique isn't more popular, JSON is quite often used as a return format in REST-style services and it would be nice to have a standard way of implementing transformations to it.Jadda
Link only answerEventful
This answer seems irrelevant. Best I can tell, the link above saying "a company may have implemented something suitable" ultimately leads to nothing. An additional search for XJR (the purported product) is fruitless.Survive
N
74

Try JOLT. It is a JSON to JSON transformation library written in Java.

It was created specifically because we did not want to play the "JSON -> XML -> XSLT -> XML -> JSON" game, and using a template for any sufficiently complex transform is unmaintainable.

Nellynelms answered 1/7, 2013 at 20:45 Comment(1)
+9000: This is a serious project! Huzzah. The online demo with examples greatly helps to climb the learning curve: jolt-demo.appspot.comCartogram
R
17

XSLT supports JSON as seen at http://www.w3.org/TR/xslt-30/#json

XML uses angular brackets for delimiter tokens, JSON uses braces, square brackets, ... I. e. XML's fewer token recognition comparisons means it's optimized for declarative transformation, whereas more comparisons, being like switch statement, for speed reasons assume speculative branch prediction that imperative code in scripting languages is useful for. As direct consequence, for different mixes of semi-structured data, you may want to benchmark XSLT and javascript engines' performance as part of responsive pages. For negligible data payload, transformations might work just as well with JSON without XML serialization. W3's decision ought to be based on better analysis.

Roast answered 29/9, 2013 at 18:38 Comment(1)
You can also use JSON2XML as a separate library that gives you an XSLT 3.0 representation of JSON without the need for an XSLT 3.0 processor.Drops
P
16

jq - lightweight and flexible command-line JSON processor

It's not template-based like XSLT, but more concise. e.g. to extract name and address fields into an array: [.name, .address]

The tutorial walks through an example of transforming Twitter's JSON API (and the manual has many examples).

Periclean answered 5/7, 2013 at 21:25 Comment(4)
It's more concise because it is capable of a lot less.Ideology
I did not find how to search recursively a given attribute in a Json treeTidwell
@Tidwell is .. | .attr_name? what you are looking for? (from stedolan.github.io/jq/manual/#RecursiveDescent:..)Ormazd
Maybe not as capable as XSLT but very useful and not as complicated as XSLTFibriform
N
15

I recently found a tool that I love for styling JSON: https://github.com/twigkit/tempo. Very easy tool to use--in my opinion, it is much easier to work with than XSLT--no need for XPATH queries.

Nightingale answered 9/5, 2011 at 14:14 Comment(2)
Tempo looks great if the final result of the transformation is HTML. But what if you just want to rearrange an implied structure into a different one, yet the final result is still JSON. I would still want an analog of XPath so I can write the transformation in a functional way.Comeuppance
Tempo is very interesting indeed thank you. However you can send a xml to the browser and a xslt (<? xsl-stylesheet>) and your browser will apply the xslt to the xml, showing a defined view of your xml without any further code. This should be the case for jsonT / tempo , too.Agronomics
P
13

Have a look at jsonpath-object-transform

Peracid answered 13/2, 2015 at 16:36 Comment(0)
A
12

I wrote my own small library around this, recently, which tries to stay as close to

5.1 Processing Model (XSLT REC) https://www.w3.org/TR/xslt#section-Processing-Model

as is possible (as I could anyway), in a few lines of JavaScript code.

Here are a few not-completely-trivial examples of use...

1. JSON-to-some-markup:

Fiddle: https://jsfiddle.net/YSharpLanguage/kj9pk8oz/10

(inspired by D.1 Document Example (XSLT REC) https://www.w3.org/TR/xslt#section-Document-Example)

where this:

var D1document = {
    type: "document", title: [ "Document Title" ],
    "": [
      { type: "chapter", title: [ "Chapter Title" ],
        "": [
        { type: "section", title: [ "Section Title" ],
          "": [
            { type: "para", "": [ "This is a test." ] },
            { type: "note", "": [ "This is a note." ] }
        ] },
        { type: "section", title: [ "Another Section Title" ],
          "": [
            { type: "para", "": [ "This is ", { emph: "another" }, " test." ] },
            { type: "note", "": [ "This is another note." ] }
        ] }
      ] }
    ] };

var D1toHTML = { $: [
  [ [ function(node) { return node.type === "document"; } ],
    function(root) {
      return "<html>\r\n\
  <head>\r\n\
    <title>\r\n\
      {title}\r\n".of(root) + "\
    </title>\r\n\
  </head>\r\n\
  <body>\r\n\
{*}".of(root[""].through(this)) + "\
  </body>\r\n\
</html>";
    }
  ],
  [ [ function(node) { return node.type === "chapter"; } ],
    function(chapter) {
      return "    <h2>{title}</h2>\r\n".of(chapter) + "{*}".of(chapter[""].through(this));
    }
  ],
  [ [ function(node) { return node.type === "section"; } ],
    function(section) {
      return "    <h3>{title}</h3>\r\n".of(section) + "{*}".of(section[""].through(this));
    }
  ],
  [ [ function(node) { return node.type === "para"; } ],
    function(para) {
      return "    <p>{*}</p>\r\n".of(para[""].through(this));
    }
  ],
  [ [ function(node) { return node.type === "note"; } ],
    function(note) {
      return '    <p class="note"><b>NOTE: </b>{*}</p>\r\n'.of(note[""].through(this));
    }
  ],
  [ [ function(node) { return node.emph; } ],
    function(emph) {
      return "<em>{emph}</em>".of(emph);
    }
  ]
] };

console.log(D1document.through(D1toHTML));

... gives:

<html>
  <head>
    <title>
      Document Title
    </title>
  </head>
  <body>
    <h2>Chapter Title</h2>
    <h3>Section Title</h3>
    <p>This is a test.</p>
    <p class="note"><b>NOTE: </b>This is a note.</p>
    <h3>Another Section Title</h3>
    <p>This is <em>another</em> test.</p>
    <p class="note"><b>NOTE: </b>This is another note.</p>
  </body>
</html>

and

2. JSON-to-JSON:

Fiddle: https://jsfiddle.net/YSharpLanguage/ppfmmu15/10

where this:

// (A "Company" is just an object with a "Team")
function Company(obj) {
  return obj.team && Team(obj.team);
}

// (A "Team" is just a non-empty array that contains at least one "Member")
function Team(obj) {
  return ({ }.toString.call(obj) === "[object Array]") &&
         obj.length &&
         obj.find(function(item) { return Member(item); });
}

// (A "Member" must have first and last names, and a gender)
function Member(obj) {
  return obj.first && obj.last && obj.sex;
}

function Dude(obj) {
  return Member(obj) && (obj.sex === "Male");
}

function Girl(obj) {
  return Member(obj) && (obj.sex === "Female");
}

var data = { team: [
  { first: "John", last: "Smith", sex: "Male" },
  { first: "Vaio", last: "Sony" },
  { first: "Anna", last: "Smith", sex: "Female" },
  { first: "Peter", last: "Olsen", sex: "Male" }
] };

var TO_SOMETHING_ELSE = { $: [

  [ [ Company ],
    function(company) {
      return { some_virtual_dom: {
        the_dudes: { ul: company.team.select(Dude).through(this) },
        the_grrls: { ul: company.team.select(Girl).through(this) }
      } }
    } ],

  [ [ Member ],
    function(member) {
      return { li: "{first} {last} ({sex})".of(member) };
    } ]

] };

console.log(JSON.stringify(data.through(TO_SOMETHING_ELSE), null, 4));

... gives:

{
    "some_virtual_dom": {
        "the_dudes": {
            "ul": [
                {
                    "li": "John Smith (Male)"
                },
                {
                    "li": "Peter Olsen (Male)"
                }
            ]
        },
        "the_grrls": {
            "ul": [
                {
                    "li": "Anna Smith (Female)"
                }
            ]
        }
    }
}

3. XSLT vs. JavaScript:

A JavaScript equivalent of...

XSLT 3.0 REC Section 14.4 Example: Grouping Nodes based on Common Values

(at: http://jsfiddle.net/YSharpLanguage/8bqcd0ey/1)

Cf. https://www.w3.org/TR/xslt-30/#grouping-examples

where...

var cities = [
  { name: "Milano",  country: "Italia",      pop: 5 },
  { name: "Paris",   country: "France",      pop: 7 },
  { name: "München", country: "Deutschland", pop: 4 },
  { name: "Lyon",    country: "France",      pop: 2 },
  { name: "Venezia", country: "Italia",      pop: 1 }
];

/*
  Cf.
  XSLT 3.0 REC Section 14.4
  Example: Grouping Nodes based on Common Values

  https://www.w3.org/TR/xslt-30/#grouping-examples
*/
var output = "<table>\r\n\
  <tr>\r\n\
    <th>Position</th>\r\n\
    <th>Country</th>\r\n\
    <th>City List</th>\r\n\
    <th>Population</th>\r\n\
  </tr>{*}\r\n\
</table>".of
  (
    cities.select().groupBy("country")(function(byCountry, index) {
      var country = byCountry[0],
          cities = byCountry[1].select().orderBy("name");
      return "\r\n\
  <tr>\r\n\
    <td>{position}</td>\r\n\
    <td>{country}</td>\r\n\
    <td>{cities}</td>\r\n\
    <td>{population}</td>\r\n\
  </tr>".
        of({ position: index + 1, country: country,
             cities: cities.map(function(city) { return city.name; }).join(", "),
             population: cities.reduce(function(sum, city) { return sum += city.pop; }, 0)
           });
    })
  );

... gives:

<table>
  <tr>
    <th>Position</th>
    <th>Country</th>
    <th>City List</th>
    <th>Population</th>
  </tr>
  <tr>
    <td>1</td>
    <td>Italia</td>
    <td>Milano, Venezia</td>
    <td>6</td>
  </tr>
  <tr>
    <td>2</td>
    <td>France</td>
    <td>Lyon, Paris</td>
    <td>9</td>
  </tr>
  <tr>
    <td>3</td>
    <td>Deutschland</td>
    <td>München</td>
    <td>4</td>
  </tr>
</table>

4. JSONiq vs. JavaScript:

A JavaScript equivalent of...

JSONiq Use Cases Section 1.1.2. Grouping Queries for JSON

(at: https://jsfiddle.net/YSharpLanguage/hvo24hmk/3)

Cf. http://jsoniq.org/docs/JSONiq-usecases/html-single/index.html#jsongrouping

where...

/*
  1.1.2. Grouping Queries for JSON
  http://jsoniq.org/docs/JSONiq-usecases/html-single/index.html#jsongrouping
*/
var sales = [
  { "product" : "broiler", "store number" : 1, "quantity" : 20  },
  { "product" : "toaster", "store number" : 2, "quantity" : 100 },
  { "product" : "toaster", "store number" : 2, "quantity" : 50 },
  { "product" : "toaster", "store number" : 3, "quantity" : 50 },
  { "product" : "blender", "store number" : 3, "quantity" : 100 },
  { "product" : "blender", "store number" : 3, "quantity" : 150 },
  { "product" : "socks", "store number" : 1, "quantity" : 500 },
  { "product" : "socks", "store number" : 2, "quantity" : 10 },
  { "product" : "shirt", "store number" : 3, "quantity" : 10 }
];

var products = [
  { "name" : "broiler", "category" : "kitchen", "price" : 100, "cost" : 70 },
  { "name" : "toaster", "category" : "kitchen", "price" : 30, "cost" : 10 },
  { "name" : "blender", "category" : "kitchen", "price" : 50, "cost" : 25 },
  {  "name" : "socks", "category" : "clothes", "price" : 5, "cost" : 2 },
  { "name" : "shirt", "category" : "clothes", "price" : 10, "cost" : 3 }
];

var stores = [
  { "store number" : 1, "state" : "CA" },
  { "store number" : 2, "state" : "CA" },
  { "store number" : 3, "state" : "MA" },
  { "store number" : 4, "state" : "MA" }
];

var nestedGroupingAndAggregate = stores.select().orderBy("state").groupBy("state")
( function(byState) {
    var state = byState[0],
        stateStores = byState[1];
    byState = { };
    return (
      (
        byState[state] =
        products.select().orderBy("category").groupBy("category")
        ( function(byCategory) {
            var category = byCategory[0],
                categoryProducts = byCategory[1],
                categorySales = sales.filter(function(sale) {
                  return stateStores.find(function(store) { return sale["store number"] === store["store number"]; }) &&
                         categoryProducts.find(function(product) { return sale.product === product.name; });
                });
            byCategory = { };
            return (
              (
                byCategory[category] =
                categorySales.select().orderBy("product").groupBy("product")
                ( function(byProduct) {
                    var soldProduct = byProduct[0],
                        soldQuantities = byProduct[1];
                    byProduct = { };
                    return (
                      (
                        byProduct[soldProduct] =
                        soldQuantities.reduce(function(sum, sale) { return sum += sale.quantity; }, 0)
                      ),
                      byProduct
                    );
                } ) // byProduct()
              ),
              byCategory
            );
        } ) // byCategory()
      ),
      byState
    );
} ); // byState()

... gives:

[
  {
    "CA": [
      {
        "clothes": [
          {
            "socks": 510
          }
        ]
      },
      {
        "kitchen": [
          {
            "broiler": 20
          },
          {
            "toaster": 150
          }
        ]
      }
    ]
  },
  {
    "MA": [
      {
        "clothes": [
          {
            "shirt": 10
          }
        ]
      },
      {
        "kitchen": [
          {
            "blender": 250
          },
          {
            "toaster": 50
          }
        ]
      }
    ]
  }
]

It is also useful to overcome the limitations of JSONPath wrt. querying against the ancestor axis, as raised by this SO question (and certainly others).

E.g., how to get the discount of a grocery item knowing its brand id, in

{
 "prods": [
    {
        "info": {
              "rate": 85
                },
        "grocery": [
                 {
                  "brand": "C",
                  "brand_id": "984"
                 },
                 {
                  "brand": "D",
                  "brand_id": "254"
                 }
                 ],
         "discount": "15"
    },
    {
        "info": {
              "rate": 100
                },
        "grocery": [
                 {
                  "brand": "A",
                  "brand_id": "983"
                 },
                 {
                  "brand": "B",
                  "brand_id": "253"
                 }
                 ],
         "discount": "20"
     }
 ]
}

?

A possible solution is:

var products = {
     "prods": [
        {
            "info": {
                  "rate": 85
                    },
            "grocery": [
                     {
                      "brand": "C",
                      "brand_id": "984"
                     },
                     {
                      "brand": "D",
                      "brand_id": "254"
                     }
                     ],
             "discount": "15"
        },
        {
            "info": {
                  "rate": 100
                    },
            "grocery": [
                     {
                      "brand": "A",
                      "brand_id": "983"
                     },
                     {
                      "brand": "B",
                      "brand_id": "253"
                     }
                     ],
             "discount": "20"
         }
     ]
};

function GroceryItem(obj) {
  return (typeof obj.brand === "string") && (typeof obj.brand_id === "string");
}

    // last parameter set to "true", to grab all the "GroceryItem" instances
    // at any depth:
var itemsAndDiscounts = [ products ].nodeset(GroceryItem, true).
    map(
      function(node) {
        var item = node.value, // node.value: the current "GroceryItem" (aka "$.prods[*].grocery[*]")

            discount = node.parent. // node.parent: the array of "GroceryItem" (aka "$.prods[*].grocery")
                       parent. // node.parent.parent: the product (aka "$.prods[*]")
                       discount; // node.parent.parent.discount: the product discount

        // finally, project into an easy-to-filter form:
        return { id: item.brand_id, discount: discount };
      }
    ),
    discountOfItem983;

discountOfItem983 = itemsAndDiscounts.
  filter
  (
    function(mapped) {
      return mapped.id === "983";
    }
  )
  [0].discount;

console.log("Discount of #983: " + discountOfItem983);

... which gives:

Discount of #983: 20

'HTH,

Akanke answered 26/2, 2016 at 23:6 Comment(0)
F
11

To say lack of tools suggest lack of need is just begging the question. The same could be applied to support for X or Y in Linux (Why bother developing quality drivers and/or games for such a minority OS? And why pay attention to an OS that big game and hardware companies don't develop for?). Probably the people who would need to use XSLT and JSON end up using a somewhat trivial workaround: Transforming JSON into XML. But that's not the optimal solution, is it?

When you have a native JSON format and you want to edit it "wysywyg" in the browser, XSLT would be a more than adequate solution for the problem. Doing that with traditional javascript programming can become a pain in the arse.

In fact, I have implemented a "stone-age" approach to XSLT, using substring parsing to interpret some basic commands for javascript, like calling a template, process children, etc. Certainly implementing a transformation engine with a JSON object is much easier than implementing a full-fledged XML parser to parse the XSLT. Problem is, that to use XML templates to transform a JSON object you need to parse the XML of the templates.

To tranform a JSON object with XML (or HTML, or text or whatever) you need to think carefully about the syntax and what special characters you need to use to identify the transformation commands. Otherwise you'll end up having to design a parser for your own custom templating language. Having walked through that path, I can tell you that it's not pretty.

Update (Nov 12, 2010): After a couple of weeks working on my parser, I've been able to optimize it. Templates are parsed beforehand and commands are stored as JSON objects. Transformation rules are also JSON objects, while the template code is a mix of HTML and a homebrew syntax similar to shell code. I've been able to transform a complex JSON document into HTML to make a document editor. The code is around 1K lines for the editor (it's for a private project so I can't share it) and around 990 lines for the JSON transformation code (includes iteration commands, simple comparisons, template calling, variable saving and evaluation). I plan to release it under a MIT license. Drop me a mail if you want to get involved.

Footsie answered 2/11, 2010 at 16:11 Comment(0)
N
11

As yet another new answer to an old question, I'd suggest a look at DefiantJS. It's not an XSLT equivalent for JSON, it is XSLT for JSON. The "Templating" section of the documentation includes this example:

<!-- Defiant template -->
<script type="defiant/xsl-template">
    <xsl:template name="books_template">
        <xsl:for-each select="//movie">
            <xsl:value-of select="title"/><br/>
        </xsl:for-each>
    </xsl:template>
</script>

<script type="text/javascript">

var data = {
        "movie": [
            {"title": "The Usual Suspects"},
            {"title": "Pulp Fiction"},
            {"title": "Independence Day"}
        ]
    },
    htm = Defiant.render('books_template', data);

console.log(htm);
// The Usual Suspects<br>
// Pulp Fiction<br>
// Independence Day<br>
Neille answered 20/1, 2016 at 21:15 Comment(1)
Oof requiring a browser dom is a showstopper though. (Puppeteer really isnt a good option in a high-load ETL server)Reinforce
S
9

There is now! 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.

Spoonbill answered 10/7, 2016 at 17:42 Comment(0)
M
5

I've been really tired of the enormous amount of JavaScript templating engines out there, and all their inline HTML-templates, different markup styles, etc., and decided to build a small library that enables XSLT formatting for JSON data structures. Not rocket science in any way -- it's just JSON parsed to XML and then formatted with a XSLT document. It's fast too, not as fast as JavaScript template engines in Chrome, but in most other browsers it's at least as fast as the JS engine alternative for larger data structures.

Megara answered 10/5, 2012 at 7:36 Comment(0)
S
4

I am using Camel route umarshal(xmljson) -> to(xlst) -> marshal(xmljson). Efficient enough (though not 100% perfect), but simple, if you are already using Camel.

Soule answered 31/7, 2014 at 18:16 Comment(0)
V
4

JSLT is very close to a JSON equivalent of XSLT. It's a transform language where you write the fixed part of the output in JSON syntax, then insert expressions to compute the values you want to insert in the template.

An example:

{
  "time": round(parse-time(.published, "yyyy-MM-dd'T'HH:mm:ssX") * 1000),
  "device_manufacturer": .device.manufacturer,
  "device_model": .device.model,
  "language": .device.acceptLanguage
}

It's implemented in Java on top of Jackson.

Vancevancleave answered 12/10, 2018 at 6:5 Comment(0)
R
3

JSONiq is such a standard and Zorba an open-source C++ implementation. JSONiq can also be seen as XQuery with adding JSON as a native data type.

Revocation answered 21/7, 2014 at 8:0 Comment(0)
K
2

it is very possible to convert JSON using XSLT: you need JSON2SAX deserializer and SAX2JSON serializer.

Sample code in Java: http://www.gerixsoft.com/blog/json/xslt4json

Klingensmith answered 20/2, 2012 at 15:43 Comment(0)
U
1

Yate (https://github.com/pasaran/yate) is specifically designed after XSLT, features JPath (a natural XPath equivalent for JS), compiles to JavaScript and has quite a history of production use. It’s practically undocumented, but reading through samples and tests should be enough.

Uninspired answered 22/3, 2017 at 4:25 Comment(0)
P
1

I know this is an old post, but I just came across it and, though I'm biased because I wrote the tool originally, I think I have the answer that most fits the question.

The language, DTL, or Data Transformation Language is specifically designed for transforming JSON (or any structured data). It is very similar in purpose to XSLT You can find it on npm as dtl-js or on the website getdtl.org which includes a live in-browser tool to explore it.

It looks like this:

{ 
  "out": { 
    "id": "(: $user_id :)", 
    "personalInfo": { 
      "name": "(: &( $first_name ' ' $last_name ) :)", 
      "age": "(: $age :)" 
    }, 
    "contactDetails": { 
      "email": "(: $email :)", 
      "phone": "(: $phone :)" 
    }, 
    "address": { 
      "street": "(: $address1 :)", 
      "unit": "(: $address2 :)", 
      "city": "(: $addr_city :)", 
      "postal_code": "(: $addr_zip :)" 
    } 
  } 
}

DTL and XSLT are alike in several important ways:

  1. Purpose for Data Transformation: Both XSLT and DTL are primarily used for transforming data. They take input data and transform it into a desired output format.

  2. Support for Complex Transformations: Both can handle complex data transformations, allowing users to manipulate and restructure data in sophisticated ways.

  3. Embeddability: Both can be embedded within the data they transform. DTL can be embedded within JSON structures, much like XSLT within XML files.

  4. Rule-Based Logic: They operate based on defined rules or patterns - XSLT uses template rules for XML elements, whereas DTL uses transformation expressions for JSON elements.

  5. Pattern Matching: XSLT utilizes XPath for selecting XML nodes, and similarly, DTL employs expressions to select and manipulate JSON elements based on certain criteria.

  6. Declarative Nature: Both languages are declarative, focusing on what the transformation should achieve rather than how it should be done.

  7. Iterative and Conditional Processing: They support conditional logic and iteration - XSLT with constructs like xsl:for-each and xsl:if, and DTL with helpers like map and grep.

  8. Extensibility: Both can be extended with additional functionalities, like custom functions in XSLT and custom helpers in DTL.

  9. Transformation Context Awareness: Each has a context-aware transformation process, essential for applying the correct transformation logic to the data.

  10. Data Agnostic Logic: The transformation logic in both is data-agnostic, allowing it to be applied to different sets of data that conform to the expected structure.

In short, DTL is tailored for JSON and is specifically designed to do exactly the same job for JSON that XSLT does for XML.

Again, I'm biased, but some important ways DTL is different from XSLT:

  1. Easier to Understand: DTL transform structure usually mirrors the desired output structure and the syntax is much simpler to understand and read. (You can look at the quickstart which tells you everything you need to know.)

  2. Data Format Flexibility: While DTL is primarily designed for JSON, it can also handle YAML, CSV, and even plain text data. This versatility makes DTL more suitable for a variety of data formats beyond XML. The dtl command line tool works with multiple formats for bulk data processing.

  3. Integration with Modern Web Technologies: DTL integrates seamlessly with JavaScript and modern web development practices, making it a natural fit for web applications that predominantly use JSON. It runs on the server, in the browser, and has even been used directly in NoSQL databases like MongoDB.

  4. Ease of Debugging: The simpler syntax and structure of DTL can make debugging easier, especially when used within JavaScript environments. The interactive tool on the website and the dtlr command line REPL / data explorer make it easy to figure out what you need to do.

  5. Readability and Maintenance: DTL's straightforward syntax and JSON-like structure can make DTL transformations more readable and maintainable.

  6. Strong editor support: DTL has strong editor support, providing syntax highlighting and snippets for all built-in functionality in vs-code, sublime-text and vim.

Again, I wrote it originally so I'm biased, but I wrote it because there wasn't a good answer for this question. It's stable and maintained and it's been in production use since 2013 by many companies and it's very good at what it does.

Hope it helps.

Prescription answered 14/12, 2023 at 23:34 Comment(0)
J
0

Why don't you converts JSON to XML using Mr. Data Coverter , tranform it using XSLT and then change it back to JSON using the same.

Jaundice answered 1/1, 2013 at 5:36 Comment(1)
That's not an option if you want to have your code do it for you with good performance.Priestly
B
0

For a working doodle/proof of concept of an approach to utilize pure JavaScript along with the familiar and declarative pattern behind XSLT's matching expressions and recursive templates, see https://gist.github.com/brettz9/0e661b3093764f496e36

(A similar approach might be taken for JSON.)

Note that the demo also relies on JavaScript 1.8 expression closures for convenience in expressing templates in Firefox (at least until the ES6 short form for methods may be implemented).

Disclaimer: This is my own code.

Bonney answered 2/12, 2014 at 8:38 Comment(0)
S
0

I wrote a dom adapter for my jackson based json processing framework long time ago. It uses the nu.xom library. The resulting dom tree works with the java xpath and xslt facilities. I made some implementation choices that are pretty straightforward. For example the root node is always called "root", arrays go into an ol node with li sub elements (like in html), and everything else is just a sub node with a primitive value or another object node.

JsonXmlConverter.java

Usage: JsonObject sampleJson = sampleJson(); org.w3c.dom.Document domNode = JsonXmlConverter.getW3cDocument(sampleJson, "root");

Sadomasochism answered 16/12, 2016 at 9:29 Comment(0)
K
0

One approach not yet given is to use a parser generator to create a parser in XSLT which parses JSON and produces an XML output.

One option that gets mentioned a lot at the XML conferences is the ReX parser generator (http://www.bottlecaps.de/rex/) - although totally undocumented on the site, recipes are available on searching.

Keever answered 7/7, 2017 at 19:34 Comment(0)
R
0

It may be possible to use XSLT with JSON. Verson 3 of XPath(3.1) XSLT(3.0) and XQuery(3.1) supports JSON in some way. This seems to be available in the commercial version of Saxon, and might at some point be included in the HE version. https://www.saxonica.com/html/documentation/functions/fn/parse-json.html

-

What I would expect from an alternative solution:

I would want to be able input JSON to fetch a matching set of data, and output JSON or TEXT.

Access arbitrary properties and evaluate the values

Support for conditional logic

I would want the transformation scripts to be external from the tool, text based, and preferably a commonly used language.

Potential alternative?

I wonder if SQL could be a suitable alternative. https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server

It would be nice if the alternative tool could handle JSON and XML https://learn.microsoft.com/en-us/sql/relational-databases/xml/openxml-sql-server

I have not yet tried to convert the XSLT scripts I use to SQL, or fully evaluated this option yet, but I hope to look into it more soon. Just some thoughts so far.

Redshank answered 16/11, 2017 at 15:49 Comment(0)
H
0

CsvCruncher

Despite it's name does not suggest it, CsvCruncher can be efficiently used to read tabular* JSON data and process them using SQL.

* Tabular means that some part of the JSON tree is regular, i.e. one to many entries of same or similar structure.

CsvCruncher turns this part of the tree into a SQL table, and then you have a full SQL syntax to query it.
You may also load multiple JSON trees, and then have multiple tables to play with.
You may also store the SQL result as a JSON (or CSV) and then process it again.

This has served me way better than really complicated XSLT approach (despite it's a lot of fun when you really know it deeply).

Disclaimer: I am the developer of CsvCruncher.

Hasdrubal answered 24/11, 2022 at 10:55 Comment(0)
T
-4

Not too sure there is need for this, and to me lack of tools suggests lack of need. JSON is best processed as objects (the way it's done in JS anyway), and you typically use language of the objects itself to do transformations (Java for Java objects created from JSON, same for Perl, Python, Perl, c#, PHP and so on). Just with normal assignments (or set, get), looping and so on.

I mean, XSLT is just another language, and one reason it is needed is that XML is not an object notation and thus objects of programming languages are not exact fits (impedance between hierarchic xml model and objects/structs).

Theoretics answered 29/9, 2010 at 5:6 Comment(7)
After Facebook converted from XML to Json, I desperately need a tool like this.Affranchise
What use case are you thinking? Is it to be able to render JSON content similar how you'd render XML responses as HTML? Or something different?Theoretics
I wonder how easy it would be to manipulate JSON transformation the programmatic object way (w/ looping, branching as needed, etc.) vs using XSLT type method, particularly in case of transforming massive JSON object and where some data in source JSON is shifted up/down some nodes in the target JSON (so not simply a direct copy of the structure) and say where a particular node in source or target JSON is part of object array within the JSON and the other JSON (source/target) is not.Disease
Ease is very subjective, so I suspect much of it has to do with what one is used to.Theoretics
While there's definitely a need for JSON transformation, you're right it's largely met by JS. :-) But have you seen jq - lightweight and flexible command-line JSON processor? Especially for when JS isn't available. I would say transformation is dramatically easier and more intuitive, even than JS. e.g. to extract the fields name and address, and put them in an array: [.name, .address]Periclean
@Periclean I have heard good things about jq, and I concur that there is need for some sort of sort of loosely typed processing, similar to SQL (filtering, projection). So maybe it's more "not quite like XSLT, since that's for XML, but something that's more geared towards JSON data/object model".Theoretics
I propose to delete this, as it is not an answer, rather a polemic and guessing about the OP's needs.Cursorial

© 2022 - 2024 — McMap. All rights reserved.