There is an ongoing outage on the primary CPAN mirror. It is possible to work around the issue by using MetaCPAN as a mirror.

NAME

JQ::Lite - A lightweight jq-like JSON query engine in Perl

VERSION

Version 0.69

SYNOPSIS

use JQ::Lite;

my $jq = JQ::Lite->new;
my @results = $jq->run_query($json_text, '.users[].name');

for my $r (@results) {
    print encode_json($r), "\n";
}

DESCRIPTION

JQ::Lite is a lightweight, pure-Perl JSON query engine inspired by the jq command-line tool.

It allows you to extract, traverse, and filter JSON data using a simplified jq-like syntax — entirely within Perl, with no external binaries or XS modules.

FEATURES

  • Pure Perl (no XS, no external binaries required)

  • Dot notation traversal (e.g. .users[].name)

  • Optional key access using '?' (e.g. .nickname?)

  • Array indexing and flattening (.users[0], .users[])

  • Boolean filters via select(...) with ==, !=, <, >, and, or

  • Pipe-style query chaining using | operator

  • Built-in functions: length, keys, values, first, last, reverse, sort, sort_desc, sort_by, unique, unique_by, has, contains, group_by, group_count, join, split, count, empty, type, nth, del, compact, upper, lower, titlecase, abs, ceil, floor, trim, substr, slice, startswith, endswith, add, sum, sum_by, product, min, max, avg, median, stddev, drop, chunks, flatten_all

  • Supports map(...), limit(n), drop(n), and chunks(n) style transformations

  • Interactive mode for exploring queries line-by-line

  • Command-line interface: jq-lite (compatible with stdin or file)

  • Decoder selection via --use (JSON::PP, JSON::XS, etc.)

  • Debug output via --debug

  • List all functions with --help-functions

CONSTRUCTOR

new

my $jq = JQ::Lite->new;

Creates a new instance. Options may be added in future versions.

METHODS

run_query

my @results = $jq->run_query($json_text, $query);

Runs a jq-like query against the given JSON string. Returns a list of matched results. Each result is a Perl scalar (string, number, arrayref, hashref, etc.) depending on the query.

SUPPORTED SYNTAX

  • .key.subkey

  • .array[0] (index access)

  • .array[] (flattening arrays)

  • .key? (optional key access)

  • select(.key > 1 and .key2 == "foo") (boolean filters)

  • group_by(.field) (group array items by key)

  • group_count(.field) (tally items by key)

  • sum_by(.field) (sum numeric values projected from each array item)

  • sort_desc()

    Sort array elements in descending order using smart numeric/string comparison.

    Example:

    .scores | sort_desc

    Returns:

    [100, 75, 42, 12]
  • sort_by(.key) (sort array of objects by key)

  • unique_by(.key) (remove duplicates based on a projected key)

  • .key | count (count items or fields)

  • .[] | select(...) | count (combine flattening + filter + count)

  • .array | map(.field) | join(", ")

    Concatenates array elements with a custom separator string. Example:

    .users | map(.name) | join(", ")

    Results in:

    "Alice, Bob, Carol"
  • split(separator)

    Split string values (and arrays of strings) using a literal separator. Example:

    .users[0].name | split("")

    Results in:

    ["A", "l", "i", "c", "e"]
  • values()

    Returns all values of a hash as an array. Example:

    .profile | values
  • empty()

    Discards all output. Compatible with jq. Useful when only side effects or filtering is needed without output.

    Example:

    .users[] | select(.age > 25) | empty
  • .[] as alias for flattening top-level arrays

  • flatten_all()

    Recursively flattens nested arrays into a single-level array while preserving non-array values.

    Example:

    [[1, 2], [3, [4]]] | flatten_all

    Returns:

    [1, 2, 3, 4]
  • type()

    Returns the type of the value as a string: "string", "number", "boolean", "array", "object", or "null".

    Example:

    .name | type     # => "string"
    .tags | type     # => "array"
    .profile | type  # => "object"
  • nth(n)

    Returns the nth element (zero-based) from an array.

    Example:

    .users | nth(0)   # first user
    .users | nth(2)   # third user
  • del(key)

    Deletes a specified key from a hash object and returns a new hash without that key.

    Example:

    .profile | del("password")

    If the key does not exist, returns the original hash unchanged.

    If applied to a non-hash object, returns the object unchanged.

  • compact()

    Removes undef and null values from an array.

    Example:

    .data | compact()

    Before: [1, null, 2, null, 3]

    After: [1, 2, 3]

  • upper()

    Converts strings to uppercase. When applied to arrays, each scalar element is uppercased recursively, leaving nested hashes or booleans untouched.

    Example:

    .title | upper      # => "HELLO WORLD"
    .tags  | upper      # => ["PERL", "JSON"]
  • titlecase()

    Converts strings to title case (first letter of each word uppercase). When applied to arrays, each scalar element is transformed recursively, leaving nested hashes or booleans untouched.

    Example:

    .title | titlecase   # => "Hello World"
    .tags  | titlecase   # => ["Perl", "Json"]
  • lower()

    Converts strings to lowercase. When applied to arrays, each scalar element is lowercased recursively, leaving nested hashes or booleans untouched.

    Example:

    .title | lower      # => "hello world"
    .tags  | lower      # => ["perl", "json"]
  • contains(value)

    Checks whether the current value includes the supplied fragment.

    * For strings, returns true when the substring exists. * For arrays, returns true if any element equals the supplied value. * For hashes, returns true when the key is present.

    Example:

    .title | contains("perl")     # => true
    .tags  | contains("json")     # => true
    .meta  | contains("lang")     # => true
  • unique_by(".key")

    Removes duplicate objects (or values) from an array by projecting each entry to the supplied key path and keeping only the first occurrence of each signature. Use . to deduplicate by the entire value.

    Example:

    .users | unique_by(.name)      # => keeps first record for each name
    .tags  | unique_by(.)          # => removes duplicate scalars
  • startswith("prefix")

    Returns true if the current string (or each string inside an array) begins with the supplied prefix. Non-string values yield false.

    Example:

    .title | startswith("Hello")   # => true
    .tags  | startswith("j")       # => [false, true, false]
  • endswith("suffix")

    Returns true if the current string (or each string inside an array) ends with the supplied suffix. Non-string values yield false.

    Example:

    .title | endswith("World")     # => true
    .tags  | endswith("n")         # => [false, true, false]
  • substr(start[, length])

    Extracts a substring from the current string using zero-based indexing. When applied to arrays, each scalar element receives the same slicing arguments recursively.

    Examples:

    .title | substr(0, 5)       # => "Hello"
    .tags  | substr(-3)         # => ["erl", "SON"]
  • slice(start[, length])

    Returns a portion of the current array using zero-based indexing. Negative start values count from the end of the array. When length is omitted, the slice continues through the final element. Non-array inputs pass through unchanged so pipelines can mix scalar and array values safely.

    Examples:

    .users | slice(0, 2)        # => first two users
    .users | slice(-2)          # => last two users
  • index(value)

    Returns the zero-based index of the first occurrence of the supplied value. When the current result is an array, deep comparisons are used so nested structures (hashes, arrays, booleans) work as expected. When the current value is a string, the function returns the position of the substring, or null when not found.

    Example:

    .users | index("Alice")     # => 0
    .tags  | index("json")      # => 1
  • abs()

    Returns absolute values for numbers. Scalars are converted directly, while arrays are processed element-by-element with non-numeric entries preserved.

    Example:

    .temperature | abs      # => 12
    .deltas      | abs      # => [3, 4, 5, "n/a"]
  • ceil()

    Rounds numbers up to the nearest integer. Scalars and array elements that look like numbers are rounded upward, while other values pass through unchanged.

    Example:

    .price   | ceil     # => 20
    .changes | ceil     # => [2, -1, "n/a"]
  • floor()

    Rounds numbers down to the nearest integer. Scalars and array elements that look like numbers are rounded downward, leaving non-numeric values untouched.

    Example:

    .price   | floor    # => 19
    .changes | floor    # => [1, -2, "n/a"]
  • round()

    Rounds numbers to the nearest integer using standard rounding (half up for positive values, half down for negatives). Scalars and array elements that look like numbers are adjusted, while other values pass through unchanged.

    Example:

    .price   | round    # => 19
    .changes | round    # => [1, -2, "n/a"]
  • trim()

    Removes leading and trailing whitespace from strings. Arrays are processed recursively, while hashes and other references are left untouched.

    Example:

    .title | trim          # => "Hello World"
    .tags  | trim          # => ["perl", "json"]

COMMAND LINE USAGE

jq-lite is a CLI wrapper for this module.

cat data.json | jq-lite '.users[].name'
jq-lite '.users[] | select(.age > 25)' data.json
jq-lite -r '.users[].name' data.json
jq-lite '.[] | select(.active == true) | .name' data.json
jq-lite '.users[] | select(.age > 25) | count' data.json
jq-lite '.users | map(.name) | join(", ")'
jq-lite '.users[] | select(.age > 25) | empty'
jq-lite '.profile | values'

Interactive Mode

Omit the query to enter interactive mode:

jq-lite data.json

You can then type queries line-by-line against the same JSON input.

Decoder Selection and Debug

jq-lite --use JSON::PP --debug '.users[0].name' data.json

Show Supported Functions

jq-lite --help-functions

Displays all built-in functions and their descriptions.

REQUIREMENTS

Uses only core modules:

  • JSON::PP

Optional: JSON::XS, Cpanel::JSON::XS, JSON::MaybeXS

SEE ALSO

JSON::PP, jq

AUTHOR

Kawamura Shingo <pannakoota1@gmail.com>

LICENSE

Same as Perl itself.