gs;
use Test::More;
use JQ::Lite;
my $json = q({
"number": -10,
"numbers": [-3, 4, -5, "n/a"]
});
my $jq = JQ::Lite->new;
my @scalar = $jq->run_query($json, '.number | abs');
is($scalar[0], 10,
'abs converts scalar numbers to absolute value');
my @array = $jq->run_query($json, '.numbers | abs');
is_deeply(
$array[0],
[3, 4, 5, 'n/a'],
'abs converts numeric array entries and lea
::Lite->new;
sub run_query {
my ($json, $query) = @_;
return [ $jq->run_query($json, $query) ];
}
subtest 'pick on single object' => sub {
my $json = '{"name":"Alice","age":30,"city":
"Paris"}';
my $results = run_query($json, 'pick("name", "age")');
is_deeply(
$results->[0],
{ name => 'Alice', age => 30 },
'returns subset of keys'
);
};
subtest
ay of objects' => sub {
my $json = '{"users":[{"name":"Alice","age":30,"email":"alice@example.com"},{"name":"Bob","age":27}]}';
my $results = run_query($json, '.users | pick("name", "email"
->new;
my $simple_json = '{"a":1,"b":2}';
my @simple = $jq->run_query($simple_json, '.a, .b');
is_deeply(\@simple, [1, 2], 'comma emits multiple top-level values');
my $users_json = '[{"name":"Alice
query($users_json, '.[] | (.name, .age)');
is_deeply(\@flat, ['Alice', 30, 'Bob', 25], 'comma branches run against identical inputs in pipelines');
my @arrays = $jq->run_query($users_json, '.[] | [.n
],
'comma preserves evaluation order for each input');
my @prefixed = $jq->run_query($users_json, '(["name","age"]), (.[] | [.name, .age])');
is_deeply(
\@prefixed,
[
['name', 'a
use strict;
use warnings;
use Test::More;
use JQ::Lite;
my $json = q({
"price": 19.2,
"discount": 1.5,
"debt": -1.7,
"tiny": -0.2,
"numbers": [1.49, 1.5, -1.49, -1.5, "n/a", null, [2.6, -2.
@round_price = $jq->run_query($json, '.price | round');
is($round_price[0], 19, 'round rounds positive scalar down when < .5');
my @round_discount = $jq->run_query($json, '.discount | round');
is($ro
nd_debt = $jq->run_query($json, '.debt | round');
is($round_debt[0], -2, 'round rounds negative scalar away from zero when beyond -.5');
my @round_tiny = $jq->run_query($json, '.tiny | round');
is($r
e;
my $json_objects = q([
{ "value": 10 },
{ "value": 30 },
{ "value": 20 }
]);
my $json_even = q([
{ "score": 1 },
{ "score": 3 },
{ "score": 5 },
{ "score": 7 }
]);
my $json_mixed =
{ "score": 3 }
]);
my $json_booleans = q([
{ "flag": true },
{ "flag": false },
{ "flag": true }
]);
my $json_entire_item = q([1, "2", 3, "not a number"]);
my $json_no_numeric = q([
{ "va
dian_objects) = $jq->run_query($json_objects, 'median_by(.value)');
is($median_objects, 20, 'median_by over projected values');
my ($median_even) = $jq->run_query($json_even, 'median_by(.score)');
is
use strict;
use warnings;
use Test::More;
use JQ::Lite;
my $json = q({
"title": "Hello World",
"tags": ["Perl", "JSON", "CLI"],
"users": [
{"name": "Alice"},
{"name": "Bob"}
]
});
my
json, '.title | lower');
is($scalar_lower[0], 'hello world', 'lower converts scalar to lowercase');
my @array_upper = $jq->run_query($json, '.tags | upper');
is_deeply($array_upper[0], ['PERL', 'JSON
>run_query($json, '.users[] | .name | lower');
is_deeply(\@pipeline_lower, ['alice', 'bob'], 'lower works in pipelines with flattened arrays');
my @scalar_titlecase = $jq->run_query($json, '.title |
tests => 3;
use JSON::PP;
use JQ::Lite;
my $jq = JQ::Lite->new;
# --- 1. Existing value should not be replaced
my $json1 = '{"nickname":"alice"}';
my @result1 = $jq->run_query($json1, '.nickname |
override existing value');
# --- 2. Undefined value should be replaced
my $json2 = '{}';
my @result2 = $jq->run_query($json2, '.nickname | default("unknown")');
is($result2[0], 'unknown', 'default()
ult for missing field');
# --- 3. Null value should be replaced
my $json3 = '{"nickname":null}';
my @result3 = $jq->run_query($json3, '.nickname | default("unknown")');
is($result3[0], 'unknown', 'de
use strict;
use warnings;
use Test::More;
use JSON::PP;
use JQ::Lite;
my $json = q({
"title": "Hello World",
"tags": ["perl", "json", "cli"],
"mixed": ["abc123", null, 42, {"kind": "object"}]
}
ew;
my @prefix = $jq->run_query($json, '.title | test("^Hello")');
ok($prefix[0], 'test() matches prefix anchored regex');
my @case_sensitive = $jq->run_query($json, '.title | test("world")');
ok(!$
insensitive = $jq->run_query($json, '.title | test("world"; "i")');
ok($case_insensitive[0], 'test() honours case-insensitive flag');
my @array_map = $jq->run_query($json, '.tags | test("^p")');
is_d
use strict;
use warnings;
use Test::More;
use JSON::PP;
use JQ::Lite;
my $structure = {
profile => {
name => 'Alice',
age => 30,
emails => [
'alice@exampl
meta => {
active => JSON::PP::true,
},
},
};
my $json = encode_json($structure);
my $jq = JQ::Lite->new;
my @name = $jq->run_query($json, '.profile | getpath(["name"])');
l = $jq->run_query($json, '.profile | getpath(["emails", 1])');
is($email[0], 'alice.work@example.com', 'getpath retrieves nested array index');
my @missing = $jq->run_query($json, '.profile | getpat
in/../lib";
use JQ::Lite;
my $jq = JQ::Lite->new;
my $json_nested = <<'JSON';
[1, [2, 3], [[4], 5], 6]
JSON
my @nested = $jq->run_query($json_nested, 'flatten_all');
is_deeply($nested[0], [1, 2, 3
ten_all recursively flattens nested arrays');
my $json_mixed = <<'JSON';
[{"values":[1,2]}, [3, [4, [5]]], 6]
JSON
my @mixed = $jq->run_query($json_mixed, 'flatten_all');
is_deeply(
$mixed[0],
use strict;
use warnings;
use Test::More;
use JQ::Lite;
my $json = q({
"text": "foo,bar,baz",
"users": [
{ "name": "Alice" },
{ "name": "Bob" }
],
"mixed": ["alpha beta", null, ["inne
y @comma = $jq->run_query($json, '.text | split(",")');
is_deeply($comma[0], [qw(foo bar baz)], 'split(",") breaks comma-separated string');
my @chars = $jq->run_query($json, '.users[0].name | split(
ars[0], [qw(A l i c e)], 'split("") returns individual characters');
my @array = $jq->run_query($json, '.mixed | split(" ")');
my $expected = [
[qw(alpha beta)],
[],
[[qw(inner)]],
{
n object
my $json_object = <<'JSON';
{
"profile": {
"name": "Alice",
"password": "secret",
"tokens": ["abc", "def"]
}
}
JSON
my @result_object = $jq->run_query(
$json_object,
Delete array entries by index
my $json_array = <<'JSON';
{
"items": [
{"id": 1},
{"id": 2},
{"id": 3}
]
}
JSON
my @result_array = $jq->run_query($json_array, '.items | delpaths([[1]])
ts by index'
);
# --- 3. Removing the root path yields null
my $json_scalar = '{"keep": true}';
my @result_null = $jq->run_query($json_scalar, '. | delpaths([[]])');
ok(!defined $result_null[0], 'de
use strict;
use warnings;
use Test::More;
use JQ::Lite;
my $json = q({
"users": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@admin.com" },
{ "na
q->run_query($json, '.users[] | select(.name match "Bob")');
my @matched_admin = $jq->run_query($json, '.users[] | select(.email match "^bob@")');
my @matched_fail = $jq->run_query($json, '.users[] |
er traverses nested values' => sub {
my $json = q({
"name": "alice",
"roles": ["dev", "ops"]
});
my @results = $jq->run_query($json, 'recurse');
is(scalar @results, 5, 'e
ent');
};
subtest 'recurse with filter follows custom child relationships' => sub {
my $tree_json = q({
"name": "root",
"children": [
{ "name": "child1" },
{ "name": "
ld2", "children": [ { "name": "grand" } ] }
]
});
my @names = $jq->run_query($tree_json, 'recurse(.children[]?) | .name');
is_deeply(\@names, [ 'root', 'child1', 'child2', 'grand'
ings;
use Test::More;
use JSON::PP;
use JQ::Lite;
my $json = <<'JSON';
{
"object": {
"third": 3,
"first": 1,
"second": 2
},
"array": ["a", "b", "c"]
}
JSON
my $jq = JQ::Lite->new;
# --- objects ---
my @object = $jq->run_query($json, '.object | keys_unsorted');
my @sorted = $jq->run_query($json, '.object | keys');
# Set equality: keys_unsorted (when sorted) should equal keys
i
= $jq->run_query($json, '.array | keys_unsorted');
is_deeply($array[0], [0, 1, 2], 'keys_unsorted returns indexes for arrays');
# --- scalars ---
my @scalar = $jq->run_query($json, '.array[0] | keys
@base64 encodes objects via JSON representation');
my @array = $jq->run_query('["a","b"]', '@base64');
is_deeply(\@array, ['WyJhIiwiYiJd'], '@base64 encodes arrays via JSON representation');
done_te
ore;
use JQ::Lite;
my $json = q({
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Carol", "age": 35}
],
"tags": ["perl", "json", "cli"],
"title": "j
,
"flags": [true, false, true]
});
my $jq = JQ::Lite->new;
my @object_index = $jq->run_query($json, '.users | index({"name":"Bob","age":25})');
is(scalar @object_index, 1, 'object index returns si
ray_index = $jq->run_query($json, '.tags | index("perl")');
is($array_index[0], 0, 'scalar array search returns zero-based index');
my @string_index = $jq->run_query($json, '.title | index("lite")');
use Test::More;
use JQ::Lite;
my $json = '{"users":[{"name":"Alice"},{"name":"Bob"},{"name":"Carol"}]}';
my $jq = JQ::Lite->new;
my @result = $jq->run_query($json, '.users | map(.name) | join(", ")
::More tests => 6;
use JSON::PP;
use JQ::Lite;
my $jq = JQ::Lite->new;
# --- 1. Nested object with arrays and booleans
my $json1 = '{"user":{"name":"Alice","tags":["perl","json"],"active":true}}';
m
y @result1 = $jq->run_query($json1, 'leaf_paths');
is_deeply(
$result1[0],
[
[ 'user', 'active' ],
[ 'user', 'name' ],
[ 'user', 'tags', 0 ],
[ 'user', 'tags',
paths',
);
# --- 2. Mixed array contents with nested objects
my $json2 = '[1,{"foo":[2,null]}]';
my @result2 = $jq->run_query($json2, 'leaf_paths');
is_deeply(
$result2[0],
[
[ 0 ],
re;
use FindBin;
use lib "$FindBin::Bin/../lib";
use JQ::Lite;
my $jq = JQ::Lite->new;
my $json = <<'JSON';
[
{ "name": "Alice", "team": "A" },
{ "name": "Bob", "team": "B" },
{ "name": "Car
: "A" },
{ "name": "Dave", "team": "B" },
{ "name": "Eve", "team": "A" }
]
JSON
my @res = $jq->run_query($json, 'group_count(team)');
is_deeply(
$res[0],
{
A => 3,
B =
> 2,
},
'group_count(team) tallies members per team'
);
@res = $jq->run_query($json, 'group_count(nickname)');
is_deeply(
$res[0],
{
null => 5,
},
'group_count on mis