Module: JQ

Defined in:
lib/jq.rb,
ext/jq/jq_ext.c

Overview

JQ provides Ruby bindings for the jq JSON processor.

This gem wraps the jq C library, allowing you to apply jq filters to JSON strings directly from Ruby. It supports all standard jq operations and provides a clean Ruby API with proper error handling.

Thread Safety

This gem requires jq 1.7+ for safe multi-threaded operation. Each method call creates an isolated jq_state, and jq 1.7+ includes critical thread safety fixes (PR #2546). Safe to use from multiple threads in MRI Ruby.

Basic Usage

require 'jq'

# Simple filtering
JQ.filter('{"name":"Alice"}', '.name')
# => "\"Alice\""

# With options
JQ.filter('{"name":"Alice"}', '.name', raw_output: true)
# => "Alice"

# Multiple outputs
JQ.filter('[1,2,3]', '.[]', multiple_outputs: true)
# => ["1", "2", "3"]

Error Handling

All jq-related errors inherit from JQ::Error:

begin
  JQ.filter('invalid', '.')
rescue JQ::ParseError => e
  puts "Invalid JSON: #{e.message}"
end

See Also:

Defined Under Namespace

Classes: CompileError, Error, ParseError, RuntimeError

Constant Summary collapse

VERSION =

The gem version number

"1.0.0"

Class Method Summary collapse

Class Method Details

.filter(json, filter, **options) ⇒ String+

Apply a jq filter to JSON input and return the result.

This is the primary method for using jq from Ruby. It parses the JSON input, compiles the filter expression, executes it, and returns the result as a JSON string (or array of strings with multiple_outputs: true).

Parameters

json (String)

Valid JSON input string

filter (String)

jq filter expression (e.g., “.name”, “.[] | select(.age > 18)”)

Options

:raw_output (Boolean)

Return raw strings without JSON encoding (equivalent to jq -r). Default: false

:compact_output (Boolean)

Output compact JSON on a single line. Default: true (set to false for pretty output)

:sort_keys (Boolean)

Sort object keys alphabetically (equivalent to jq -S). Default: false

:multiple_outputs (Boolean)

Return array of all results instead of just the first. Default: false

Returns

String

JSON-encoded result (default), or raw string if raw_output: true

Array<String>

Array of results if multiple_outputs: true

Raises

JQ::ParseError

If the JSON input is invalid

JQ::CompileError

If the jq filter expression is invalid

JQ::RuntimeError

If the filter execution fails

TypeError

If arguments are not strings

Examples

# Basic filtering
JQ.filter('{"name":"Alice","age":30}', '.name')
# => "\"Alice\""

# Raw output (no JSON encoding)
JQ.filter('{"name":"Alice"}', '.name', raw_output: true)
# => "Alice"

# Pretty output
JQ.filter('{"a":1,"b":2}', '.', compact_output: false)
# => "{\n  \"a\": 1,\n  \"b\": 2\n}"

# Multiple outputs
JQ.filter('[1,2,3]', '.[]', multiple_outputs: true)
# => ["1", "2", "3"]

# Sort keys
JQ.filter('{"z":1,"a":2}', '.', sort_keys: true)
# => "{\"a\":2,\"z\":1}"

# Complex transformation
json = '[{"name":"Alice","age":30},{"name":"Bob","age":25}]'
JQ.filter(json, '[.[] | select(.age > 26) | .name]')
# => "[\"Alice\"]"

Thread Safety

This method is thread-safe with jq 1.7+ (required by this gem). Each call creates an isolated jq_state, so concurrent calls do not interfere with each other.

Returns:

  • (String, Array<String>)


252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'ext/jq/jq_ext.c', line 252

VALUE rb_jq_filter(int argc, VALUE *argv, VALUE self) {
    VALUE json_str, filter_str, opts;
    rb_scan_args(argc, argv, "2:", &json_str, &filter_str, &opts);

    Check_Type(json_str, T_STRING);
    Check_Type(filter_str, T_STRING);

    const char *json_cstr = StringValueCStr(json_str);
    const char *filter_cstr = StringValueCStr(filter_str);

    // Parse options (default to compact output)
    int raw_output = 0, compact_output = 1;
    int sort_keys = 0, multiple_outputs = 0;

    if (!NIL_P(opts)) {
        Check_Type(opts, T_HASH);
        VALUE opt;

        opt = rb_hash_aref(opts, ID2SYM(rb_intern("raw_output")));
        if (RTEST(opt)) raw_output = 1;

        opt = rb_hash_aref(opts, ID2SYM(rb_intern("compact_output")));
        if (!NIL_P(opt)) compact_output = RTEST(opt) ? 1 : 0;

        opt = rb_hash_aref(opts, ID2SYM(rb_intern("sort_keys")));
        if (RTEST(opt)) sort_keys = 1;

        opt = rb_hash_aref(opts, ID2SYM(rb_intern("multiple_outputs")));
        if (RTEST(opt)) multiple_outputs = 1;
    }

    return rb_jq_filter_impl(json_cstr, filter_cstr,
                             raw_output, compact_output,
                             sort_keys, multiple_outputs);
}

.validate_filter!(filter) ⇒ true

Validate a jq filter expression without executing it.

This method compiles the filter to check for syntax errors without requiring any JSON input. Use this to validate user-provided filters before attempting to apply them to data.

Parameters

filter (String)

jq filter expression to validate

Returns

true

Always returns true if the filter is valid

Raises

JQ::CompileError

If the filter expression is invalid

TypeError

If filter is not a string

Examples

# Valid filters return true
JQ.validate_filter!('.name')
# => true

JQ.validate_filter!('.[] | select(.age > 18)')
# => true

# Invalid filters raise CompileError
JQ.validate_filter!('. @@@ .')
# raises JQ::CompileError: Syntax error in jq filter

# Validate user input before use
user_filter = params[:filter]
begin
  JQ.validate_filter!(user_filter)
  result = JQ.filter(json, user_filter)
rescue JQ::CompileError => e
  puts "Invalid filter: #{e.message}"
end

Thread Safety

This method is thread-safe with jq 1.7+ (required by this gem).

Returns:

  • (true)


338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'ext/jq/jq_ext.c', line 338

VALUE rb_jq_validate_filter(VALUE self, VALUE filter) {
    Check_Type(filter, T_STRING);
    const char *filter_cstr = StringValueCStr(filter);

    jq_state *jq = jq_init();
    if (!jq) {
        rb_raise(rb_eJQError, "Failed to initialize jq");
    }

    if (!jq_compile(jq, filter_cstr)) {
        jv error = jq_get_error_message(jq);

        if (jv_is_valid(error) && jv_get_kind(error) == JV_KIND_STRING) {
            const char *error_msg = jv_string_value(error);
            VALUE rb_error_msg = rb_str_new_cstr(error_msg);
            jv_free(error);
            // Store C string before cleanup (StringValueCStr can raise)
            const char *error_cstr = StringValueCStr(rb_error_msg);
            jq_teardown(&jq);
            rb_raise(rb_eJQCompileError, "%s", error_cstr);
        }

        jv_free(error);
        jq_teardown(&jq);
        rb_raise(rb_eJQCompileError, "Syntax error in jq filter");
    }

    jq_teardown(&jq);
    return Qtrue;
}