lloyd.io is the personal website of Lloyd Hilaiel, a software engineer who works for Team Ozlo and lives in Denver.

All the stuff you'll find here is available under a CC BY-SA 3.0 license (use it and change it, just don't lie about who wrote it). Icons on this site are commercially available from steedicons.com. Fonts used are available in Google's Web Font directory, and I'm using Ubuntu and Lekton. Finally, Jekyll is used for site rendering.

Finally, Atul, Pascal, and Stephen inspired the site's design. And in case you're interested, this site's code is available on github.

JSONSelect Grows Up
2011-06-02 00:00:00 -0700

JSONSelect is a query language for JSON. With JSONSelect you can write small patterns that match against JSON documents. The language is mostly an adaptation of CSS to JSON, motivated by the belief that CSS does a fine job and is widely understood.

I announced JSONSelect about two weeks ago, and there’s been some significant additions since. This blog post will provide a quick overview of the main changes to the language with examples.

If you’re interested in the really short version, go to the online demo and check out the last five selectors.

nth and objects

An oversight in the first release of JSONSelect allowed pseudo functions which rely on ordering to apply to objects. Specifically given a JSON object:

{
    "foo": 1,
    "bar": 2
}

You could write a pattern which would attempt to match one of its children based on its position, such as object:first-child. The problem with this (first pointed out by Robert Sayre) is that by definition, a JSON object is unordered. This simplest way to solve this problem is to change the nth family of pseudo-classes so that they never apply to object children. No interesting use cases are broken, and this whole issue is avoided.

If you’re interested in reading more, you can review the bug report, and check out Dave Herman’s deeper exploration of JSON semantics;

checking values with :expr()

The first release of JSONSelect had no way to conditionally match values based on their contents. It turns out there’s a whole lot you simply cannot do without basic checks on object values. Given that there’s so much less information in the structure of a JSON document vs. an HTML document, I felt the ability to test values was important for JSONSelect.

To figure out how to build in this feature I reviewed CSS3, sizzle, and sly. The only real tool I discovered was :contains(), a sizzle selector which lets you search for substrings inside a node’s contents.

JSONPath is a similar query language for JSON, and supports moderately complex expressions. A tiny expression language ultimately seemed like the most flexible solution without being overly difficult to learn or implement.

The expression language which ultimately made it into JSONSelect is influenced by CSS in that several of the available operators are inspired by those present in CSS for testing attribute values. Another area where CSS influenced JSONSelect’s expression support is in the representation of the current node’s value in an expression, which can be specified with the placeholder x. Let’s see it in action, given the following document:

{ "books": [
  { "price": 16.31,
    "name": "The Unbearable Lightness of Being",
    "author": "Milan Kundera"
  },
  { "price": 23.09,
    "name": "JavaScript Web Applications",
    "author": "Alex MacCaw"
  },
  { "price": 106.00,
    "name": "Compilers: Principles, Techniques, and Tools",
    "author": "Alfred V. Aho"
  }
] }

Here’s how you could use expressions to find all the books you could buy with the forty US dollars in your pocket:

.books .price:expr(x <= 40.00)

Now if you really strongly believed that nothing worth reading could possibly be less than a twenty spot, you could augment the expression to match your arrogance:

.books .price:expr(20 <= x && 40 >= x)

Now what was the real title of the Dragon Book? I can’t remember, but I do know that it started with Compilers:

.books .price:expr(x ^= "Compilers")

The full set of selectors supported in JSONSelect expressions is visible in the reference implementation (but you might want to refer to the latest version) and real soon now it will make it into the formal documentation.

Now, :expr() is a bit more complex than other features of JSONSelect, and for this reason two syntactic shortcuts have been added to the language to handle common tasks: :val(V) is a shorthand for :expr(x = V), and :contains(V) is a shorthand for :expr(x *= V). Perhaps a couple more shortcuts will be added as they prove themselves sufficiently common.

One thing you might have noticed, however, is that :expr() is rather useless right now. While it allows you to express constraints on values, it would only return the matching values themselves. In the above examples, what we really want are nodes related to the matched value, concretely the records of the books which match our criteria… This leads us to the next language addition:

testing descendants with :has()

Inspired by JQuery, the :has() selector allows you to perform a check against a descendant of a node. :has() in JSONSelect may contain a full selector and gives us a simple way to solve the problems discussed in the previous section. If we wanted to get the full records for all books under 20$:

.books object:has(.price:expr(x<20))

This will specifically find an object underneath the .books node that has a price property less than 20 bucks. It’s useful to point out that the object type here is redundant, as objects are the only types of nodes to which :has() can apply, so we can simplify a touch:

.books :has(.price:expr(x<20))

A naive attempt to simplify further, might yield:

:has(.price:expr(x<20))

In this case we omit the .books class that rooted the expression. This expression would match both the root document, and the object that we want, because :has() by default holds a selector that matches any descendant. A solution to this issue gives us a way to (re)introduce the :root selector:

:has(:root > .price:expr(x<20))

In case you’re not familiar with :root, it’s a CSS standard pseudo class that always matches the root of the document. With HTML this is rather useless (as resig points out), because the root of the document is more intuitively matched with html. In JSONSelect, :root is a much more important idea given there is no other way to refer to the root document.

In the above example, it’s demonstrated that when selectors inside :has() are matched against a target node, the node itself is the new document root. This feature can be used to restrict the behavior of :has() to match an object’s children (as opposed to any descendant). It may be useful at some point to add a custom has function to do this, but you be the judge, is this clearer?

:has-child(.price:expr(x<20))

:has-child is not part of JSONSelect at the moment, but we might just add it…

Now. Given all the new tools we have, we can easily express more complex ideas, like “I want all of the titles of books that are written by Authors whose names are ‘Alex MacCaw’:

:has(:root > .author:val("Alex MacCaw")) > .name

If you share my sense of aesthetics, this construct is just too complex. And that’s part of the motivation for our final new language operator:

matching siblings with ~

The CSS3 spec defines the sibling combinator. This combinator is almost totally useless in CSS given it’s directional restriction. That is, A ~ B will only match B if it occurs after A.

So a shorthand for sibling matching is interesting, and ~ is already meant for this job in CSS, but it’s going to take some adaptation given that ordering restrictions for object values are meaningless with JSON anyway (cause it’s unordered) and they also kill the utility of the operator.

So in JSONSelect, the sibling combinator (~) is simpler, it has no ordering restrictions and behaves as you might expect:

.author:val("Alex MacCaw") ~ .name

Now JSONSelect seems so complicated!

Not really. Similar to CSS, there’s some complexity and power lurking here, but you only rarely need to engage it. Ultimately to get the same level of power that other selector languages offer we’ve only had to add two pseudo functions and a new combinator (to use CSS speak). It’s worth noting that aside from the :nth-XXX family of functions there were no other changes made to the core JSONSelect language.

next up…

I’m curious to get feedback on these additions to JSONSelect, and you can kick the tires using the online demo and the reference implementation. Depending on your response, I’ll probably be slowing way down with language additions, though there may be be several convenience pseudo functions added that I alluded to above.

Provided folks are still as passionately supportive of JSONSelect, I hope to continue to incrementally refine the language while exploring some of its more interesting applications (simplifying APIs, stream filtering, and document databases).

I look forward to hearing your thoughts here, on github, and on the mailing list.