JQ-XS version 2.01 ================== JQ::XS is a clean object-oriented Perl wrapper for libjq, the C library behind the jq command-line JSON query language. From version 2.00 the jq engine is embedded: the distribution ships an upstream jq release tarball under vendor/, compiles it during "make" and links it statically, so the module does not use the libjq the operating system packages and behaves the same way everywhere. JQ::XS::jq_version() reports which jq is compiled in. FEATURES - Compile and execute jq filter programs - Process Perl data structures (hashes, arrays, numbers, strings) - Process JSON text directly - UTF-8 string support - Comprehensive error handling with Perl exceptions - Support for complex nested data structures - Multiple output values per input - Named and positional arguments, as jq's --arg/--argjson/--args - jq's output formatting: -c, --indent, --tab, -S, -a, -C, -r - The debug, stderr and input/inputs builtins, served from Perl - halt and halt_error, with the exit code and message they carry - A switch that refuses filters loading modules from disk - A known jq version on every platform, with no libjq dependency SYNOPSIS use JQ::XS; my $jq = JQ::XS->new('.foo[] | select(. > 2)'); my @results = $jq->process({ foo => [1, 3, 5] }); # Returns: (3, 5) INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires: - Perl 5.26.3 or higher - A C compiler, make, and a POSIX shell - JSON::PP No jq or libjq package is needed, at build time or at run time. The jq under vendor/ is compiled as part of "make": its release tarball is self-contained, so nothing beyond a compiler is required -- no autoconf, automake, flex, bison or python -- and the oniguruma bundled inside it is built too, so the regex builtins (test, match, capture, sub, gsub, scan, splits) work without an external library. Building it adds a few seconds to "make". To link the libjq the operating system packages instead, configure with: perl Makefile.PL JQ_SYSTEM=1 That restores the pre-2.00 behaviour and needs jq-devel (Red Hat, in the powertools/crb repository) or libjq-dev (Debian). JQ::XS::jq_version() then returns undef, and the filter semantics are whatever that libjq implements. Everything the XS calls has been in libjq since jq 1.5 except one function, jq_set_stderr_cb, which jq 1.7 added; Makefile.PL link-probes for it and says so if it is absent, in which case set_stderr_cb() croaks and the "stderr" builtin cannot be served. JQ::XS::features() reports what a given build can do. Note also that the jq 1.6 which RHEL 8 and Debian 11 ship corrupts its own heap in jq_teardown after a filter calls halt_error, aborting the process when the object is freed -- one more reason the embedded build is the default. BUILDING PACKAGES An RPM spec file is provided in rpm/perl-JQ-XS.spec and Debian packaging in debian/. To build a .deb from a source checkout: apt-get build-dep . # or: apt-get install libjson-pp-perl perl-xs-dev perl Makefile.PL make deb The .deb lands in the parent directory. "make deb_clean" removes what the build leaves behind in the source tree. The underlying command and its flags can be overridden: make deb DPKG_BUILDPACKAGE_FLAGS="-us -uc" # binary and source packages make deb DPKG_BUILDPACKAGE=debuild # run lintian afterwards To build an RPM from a source checkout: dnf builddep rpm/perl-JQ-XS.spec perl Makefile.PL make rpm "make rpm" refuses to run unless the Version: in the spec matches the module version. It builds the release tarball first, because the spec installs META.json, which only exists inside the tarball. The packages land under rpmbuild/RPMS; "make rpm_clean" removes that tree. As with deb, the tool and its flags can be overridden: make rpm RPMBUILD_FLAGS=-ba # binary and source RPMs make rpm RPMBUILD_FLAGS="-bb --nocheck" # skip the test suite RELEASES .github/workflows/release.yml runs both of the above in containers for RHEL 8, 9 and 10 and for Debian 12 and 13, and attaches the resulting packages to a GitHub release. Pushing a tag named v triggers it; running the workflow by hand builds the same packages but publishes nothing. The version is written in three places, and the workflow refuses to build unless they agree with each other and with the tag: lib/JQ/XS.pm our $VERSION = '...'; rpm/perl-JQ-XS.spec Version: debian/changelog the version in the top entry, as -1 Bump all three, and add a %changelog and a debian/changelog entry, before tagging. The Debian packages get a +debNu1 suffix added at build time so the two suites do not produce the same filename. METHODS new($program, %opts) - Compile a jq filter program process($data) - Process Perl data through the filter process_json($text, %opts) - Process JSON text through the filter program() - Get the source of the compiled program set_output(%opts) - How process_json formats its output output_options() - Read those options back halted() - Did the last run end in halt/halt_error? exit_code() - The code it halted with error_message() - The message halt_error carried die_on_halt_error($bool) - Make halt_error a Perl exception set_debug_cb(\&code) - Perl behind the debug builtin set_stderr_cb(\&code) - Perl behind the stderr builtin set_inputs($src) - Perl behind the input/inputs builtins library_paths(\@dirs) - Where include/import look for modules attr($name) - Read a jq attribute set_attr($name, $value) - Write one flags($flags) - Execution flags for each run dump_disassembly($indent) - Print the compiled bytecode new() takes vars, args, allow_includes, library_paths, attrs, flags, die_on_halt_error, debug, stderr, inputs, and the output options above. See "perldoc JQ::XS" for all of them. FUNCTIONS jq_version() - The embedded jq version, or undef under JQ_SYSTEM=1 parse_json($text) - Parse one JSON value with jq's parser parse_json_stream($text) - Parse a run of concatenated JSON values to_json($data, %opts) - Serialize with jq's printer set_colors($spec) - Colors for "color" output, in JQ_COLORS format RESTRICTING MODULE LOADING A jq program can pull definitions in from .jq files on disk with include or import, which is worth thinking about if the programs being compiled come from somewhere untrusted. allow_includes => 0 rejects any program containing one of those directives, at compile time, before jq looks at the filesystem: my $jq = JQ::XS->new($untrusted, allow_includes => 0); library_paths sets where modules are looked for, as jq's -L does, but it cannot by itself stop a program from loading anything: jq searches the current directory when a directive names no search path, and a directive carrying {search:...} does not consult the library paths at all. UPGRADING THE EMBEDDED JQ The vendored tarball is the only record of which jq this is: its name carries the version, and the build, the test suite and the release workflow all read the version back out of the filename. Nothing repeats it, so there is nothing to keep in sync. To move to a new jq: 1. Download the release tarball and its checksum from https://github.com/jqlang/jq/releases, drop the tarball in vendor/ and delete the old one -- the build refuses to guess between two. Copy the matching line out of the release's sha256sum.txt into vendor/jq-.tar.gz.sha256; the build refuses to unpack a tarball that does not match it. The line keeps upstream's bare filename, so "sha256sum -c" on it wants to be run from inside vendor/. 2. Update the two vendor/ lines in MANIFEST. 3. Refresh THIRD-PARTY-LICENSES from the new tarball's COPYING and vendor/oniguruma/COPYING, and check debian/copyright still describes what the tarball contains. 4. make jq_clean && perl Makefile.PL && make && make test t/embedded-jq.t asserts that the jq compiled in is the one vendored, so a tarball swapped without a rebuild fails the test suite rather than shipping. COPYRIGHT AND LICENSE Copyright (C) 2026 by James Rouzier This library is free software; you can redistribute it and/or modify it under the terms of the MIT license. See the LICENSE file included with this distribution.