Day 1: Slicing and dicing your JSON (App::jpath)

About the series: perlancar's 2014 Advent Calendar: Introduction to a selection of 24 modules which I published in 2014. Table of contents.

jpath (from App-jpath distribution) is a simple command-line interface wrapper for JSON::Path written by TOBYINK. With the provided jpath utility you can quickly select and transform some branch/node of a data structure. As a bonus, jpath also accepts/outputs Perl and YAML in addition to JSON.

A quick primer on JSON Path: $. to get everything as is, $.keyname to get hash value by key, $.[12] to get array element by index, $.[1,2,3] to get several elements (union), $.[*] to get all array elements, ?(...) to apply a filter (the Perl variant has some syntax modification for the filter). There are a few other constructs provided, please see the linked JSON Path page.

Some examples:

% cat bookstore.json
{
   "store" : {
      "bicycle" : [
         {
            "color" : "red",
            "price" : 19.95
         }
      ],
      "book" : [
         {
            "category" : "reference",
            "author" : "Nigel Rees",
            "title" : "Sayings of the Century",
            "price" : 8.95
         },
         {
            "category" : "fiction",
            "author" : "Evelyn Waugh",
            "title" : "Sword of Honour",
            "price" : 12.99
         },
         {
            "category" : "fiction",
            "author" : "Herman Melville",
            "title" : "Moby Dick",
            "isbn" : "0-553-21311-3",
            "price" : 8.99
         },
         {
            "category" : "fiction",
            "author" : "J. R. R. Tolkien",
            "title" : "The Lord of the Rings",
            "isbn" : "0-395-19395-8",
            "price" : 22.99
         }
      ]
   }
}

% jpath '$.' bookstore.json; # get everything as is
{
   "store" : {
      "bicycle" : [
         {
            "color" : "red",
            "price" : 19.95
         }
      ],
      "book" : [
         {
            "category" : "reference",
            "author" : "Nigel Rees",
            "title" : "Sayings of the Century",
            "price" : 8.95
         },
         {
            "category" : "fiction",
            "author" : "Evelyn Waugh",
            "title" : "Sword of Honour",
            "price" : 12.99
         },
         {
            "category" : "fiction",
            "author" : "Herman Melville",
            "title" : "Moby Dick",
            "isbn" : "0-553-21311-3",
            "price" : 8.99
         },
         {
            "category" : "fiction",
            "author" : "J. R. R. Tolkien",
            "title" : "The Lord of the Rings",
            "isbn" : "0-395-19395-8",
            "price" : 22.99
         }
      ]
   }
}

% jpath '$.store.book' bookstore.json; # get only the books
[
   [
      {
         "author": "Nigel Rees",
         "category": "reference",
         "price": 8.95,
         "title": "Sayings of the Century"
      },
      {
         "author": "Evelyn Waugh",
         "category": "fiction",
         "price": 12.99,
         "title": "Sword of Honour"
      },
      {
         "author": "Herman Melville",
         "category": "fiction",
         "isbn": "0-553-21311-3",
         "price": 8.99,
         "title": "Moby Dick"
      },
      {
         "author": "J. R. R. Tolkien",
         "category": "fiction",
         "isbn": "0-395-19395-8",
         "price": 22.99,
         "title": "The Lord of the Rings"
      }
   ]
]

% jpath '$.store.book[*]' bookstore.json; # get books, each book as separate elements
[
   {
      "author": "Nigel Rees",
      "category": "reference",
      "price": 8.95,
      "title": "Sayings of the Century"
   },
   {
      "author": "Evelyn Waugh",
      "category": "fiction",
      "price": 12.99,
      "title": "Sword of Honour"
   },
   {
      "author": "Herman Melville",
      "category": "fiction",
      "isbn": "0-553-21311-3",
      "price": 8.99,
      "title": "Moby Dick"
   },
   {
      "author": "J. R. R. Tolkien",
      "category": "fiction",
      "isbn": "0-395-19395-8",
      "price": 22.99,
      "title": "The Lord of the Rings"
   }
]

% jpath '$.store.book[]' bookstore.json; # only get the first and third books
[
   {
      "author": "Nigel Rees",
      "category": "reference",
      "price": 8.95,
      "title": "Sayings of the Century"
   },
   {
      "author": "J. R. R. Tolkien",
      "category": "fiction",
      "isbn": "0-395-19395-8",
      "price": 22.99,
      "title": "The Lord of the Rings"
   }
]

% jpath '$.store.book[?($_->{price} < 10.0)]' bookstore.json; # only get books less than 10 bucks
[
   {
      "author": "Nigel Rees",
      "category": "reference",
      "price": "8.95",
      "title": "Sayings of the Century"
   },
   {
      "author": "Herman Melville",
      "category": "fiction",
      "isbn": "0-553-21311-3",
      "price": "8.99",
      "title": "Moby Dick"
   }
]

% jpath '$.store.book[?($_->{price} < 10.0)]' bookstore.json -o perl; # output in perl
[
  {
    author   => "Nigel Rees",             # .{0}
    category => "reference",              # .{1}
    price    => 8.95,                     # .{2}
    title    => "Sayings of the Century", # .{3}
  }, # [0]
  {
    author   => "Herman Melville", # .{0}
    category => "fiction",         # .{1}
    isbn     => "0-553-21311-3",   # .{2}
    price    => 8.99,              # .{3}
    title    => "Moby Dick",       # .{4}
  }, # [1]
]

Of course, if you plan to work with lots of JSON data, most of the time you'll want to use jq instead which is much more powerful and much faster because it's written in C. But if you like JSON Path syntax and sometimes want to use Perl/YAML in addition to JSON, jpath might be useful to you.

Leave a comment

About perlancar

user-pic #perl #indonesia