An Introduction to JSON Selector
Tags: javascript, node, typescript
Published: 2020-01-25 07:57:08

*<small>Cover: https://unsplash.com/photos/l5Tzv1alcps<small>*

## What is JSON selector?

It's a library/tool that allows you to select values from JSON object using specific query syntax (or you can think of it as a **CSS Selector** but it's for JSON). 

In this post, we'll go through some of the most popular libraries and see how we can use it to select our data.

### What about Optional Chaining?
Before we begin. Let's take a look at ECMAScript's new proposal [Optional Chaining](https://tc39.es/proposal-optional-chaining/).

When working with JS Object. Most of the time we'll come across these problems.

```js
let user = { address: { city: "Bangkok" }, friends: [] }
```

How can I access `city` property safely? One could do

```js
user.address && user.address.city // "Bangkok"
```

But what if the property that we want is in much deeper level? Then you would have to do many `a && a.b && a.b.c ...`.

Thus, the birth of **Optional Chaining Operator** (`?.`)

> The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:

Let's see them in action.

```js
user?.address?.city // Bangkok
```

That's neat. It's much easier to access the nested property. But what if the target property is not an `Object`? (e.g. `Array`, etc.). Then the above solution won't work.

```js
user?.friends?. // ???
```

## JSON Selectors

The above problem can be solved with **JSON selector**. With this, we can now have more flexible and powerful ways to access our JSON object. We'll start with a basic JSON data.

### Example
Here's the typical JSON object structure that everyone is familiar with.

```js
{
  "users": [
    {
      "id": 1, "friends": [{ "name": "John"}]
    },
    {
      "id": 2, "friends": [{ "name": "Joe" }]
    }
  ]
}
```

Let's say we want to get all user ids.

```js
[1, 2]
```

We'll take a look at how to achieve the above result by exploring some of the popular libraries/tools out there.

### JSONPath

[JSONPath](https://www.npmjs.com/package/jsonpath)'s idea is to bring the power of [XPath](https://en.wikipedia.org/wiki/XPath) (XML-based path selector) to Javascript world.

#### Selecting values

With **JSONPath** we can achieve that by using the following path selector.

```js
let jp = require('jsonpath')

jp.query(users, '$.users[*].id') // [1, 2]
```

It also supports **array slice** feature. In the following example it will slice an array from 0th index to 1st index (coresponds to `array.slice(0, 1)`)

```js
jp.query(users, '$.users[:1].id') // [1]
```

One of the problem that I stumbled upon is that. It always returns array of values eventhough I explicity specify the index of an array e.g.

```js
jq.query(users, '$.users[0].id') // [1], should just return single element i.e. 1
```

There're many path expressions that you might want to check it out https://goessner.net/articles/JsonPath/index.html#e2

Here's the [playground](https://jsonpath.com/).

### jq

> [jq](https://stedolan.github.io/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

**jq** is very popular and powerful one. It can even transform your output and has become part of many command line tools. You've probably seen it in many tutorials that deal with `cli` and `json`.

Let's take a quick look.

#### Selecting values

A simple example.

```sh
$ curl <url> | jq '.users[].id' // [1, 2]
```

#### Transforming values

It can do more than just selecting. In the example below, we can even produce a new JSON output with pipe operators (`|`).

```sh
$ curl <url> | jq '.users[0] | { "id": .id }' // { "id": 1 }
```

**Explanation** It selects the first element of `users` array and then creates a new object by using `id` property of the result from previous command. 

Here's the [playground](https://jqplay.org/).

### @gizt/selector

*[@gizt/selector](https://github.com/gizt/selector) _disclaimer_: I wrote this library a while ago. Because I just want a selector with a simple syntax that every developer who uses Javascript is already familiar with.*

Let's take a quick tour.

#### Selecting values

```js
const s = require('@gizt/selector')

s('users[].id', users) // [1, 2]

// single value
$('users[0].id', users) // 1 (not [1])

// go deeper
s('users[].friends[].name', users) // ["John", "Joe"]
```

Notice that we don't have to add a prefix like `.` or `$` at the beginning of the query. 

##### Globing the keys

I believe that every developers is already familiar with `*` wildcard symbol. We can use it to filter our result even further.

**Select any properties**

In the following example, we select any properties of `user` object i.e. `id` and `friends`

```js
s('users[].*')

[
  // users[0].id
  1,
  // users[0].friends
  [{ "name": "John"}],
  // users[1].id
  2,
  // users[1].friends
  [{ "name": "Joe"}]
]
```

**Select only matched prefix/suffix**

Here we want only property that start with `i`. In this case it only matches `id`

```js
s('users[].i*') // [1, 2]
```

When we want to match a suffix we can simpy do. It'll match properties that end with `ends` i.e. `friends`

```js
s('users[].*ends') // [[ friend 1 ], [ friend 2 ]]
```

The syntax is just make any developer feels at home. No need to remember any special characters. Just plain JSON notation and `glob`.

[Playground](https://gizt.github.io/selector/)

### Summary

I just gave a quick overview on **JSON Selector** topic which covered some of the popular libraries out there (including my own :p). Some people might not aware of these tools/libraries which we can leverage to solve everyday's problems.

**P.S** it's my first article here. Hope you guys enjoy it.

If you have any libraries and feedback. Please share and comment. Have a good day :).