2015 twenty-four merry days of Perl Feed

(JSON::)Paths in the snow

JSON::Path - 2015-12-13

One thing IT elves do a lot, is to munge data structures. In some departments, sources tell us, they use JSON::Path for it. It's not an extensive tool, but it can come in handy.

Despite its name, JSON::Path does not have much to do with JSON itself. It is rather an XPath-like query mini-language for querying structured data.

For example, taking a data structure all elves are rather familiar with:

my $master_list = { children => [
    {
        name => 'Xavier',
        status => 'nice',
        wants => [
            { label => 'T.S. Moe', price => 9.99 },
            { label => 'coloring book', price => 5.45 },
        ],
    },
    {
        name => 'Alex',
        status => 'naughty',
        wants => [
            { label => 'rubber duck', price => 6.57 },
        ],
    },
    {
        name => 'Elo',
        status => 'nice',
        wants => [
            { label => 'battle tiara', price => 22.12},
            { label => 'climbing gears', price => 12.33},
        ],
    },
] };

We could extract the name of all naughty children via:

use JSON::Path qw/ jpath /;
$JSON::Path::Safe = 0;

my @naughty = jpath $master_list => '$.children[?($_->{status} eq "naughty" )].name';

Which means @naughty is now:

    [ "Alex" ]

Or get the first gift request of all nice children:

my @gifts = jpath $master_list => '$.children[?($_->{status} eq "nice" )].wants[0]';

Which means @gifts is now:

    [ "T.S. Moe", "battle tiara" ]

There is also the possibility to alter the structure directly via jpath_map. For example, if we wanted to remove the naughty children and limit the wishlist of all nice children to their least expensive item (hey, it's a tough economy for everybody), one could do:

use JSON::Path qw/ jpath_map /;

# buh-bye naughty boys and naughty girls

jpath_map {
    [ grep { $_->{status} eq 'nice' } @$_ ]
} $master_list => '$.children';

# remove the status, as they are all nice, now

jpath_map {
    delete $_->{status}; $_
} $master_list => '$.children[*]';

# let's be reasonable...

use List::MoreUtils qw/ sort_by /;

jpath_map {
    ( sort_by { $_->{price} } @$_ )[0]
} $master_list => '$.children[*].wants';

Which means $master_list finally contains:

    {
        children   [
            {
                name    "Xavier",
                wants   {
                    label   "coloring book",
                    price   5.45
                }
            },
            {
                name    "Elo",
                wants   {
                    label   "climbing gears",
                    price   12.33
                }
            }
        ]
    }

SEE ALSO

Gravatar Image This article contributed by: Yanick Champoux (yanick@cpan.org)