package JSON::JQ; use strict; use warnings; use Carp; our $VERSION = '0.09'; # internal flags our $DEBUG = 0; our $DUMP_DISASM = 0; use FindBin (); FindBin::again(); use POSIX qw/isatty/; use Path::Tiny qw/path/; use JSON qw/from_json/; # jv_print_flags in jv.h use enum qw/BITMASK:JV_PRINT_ PRETTY ASCII COLOR SORTED INVALID REFCOUNT TAB ISATTY SPACE0 SPACE1 SPACE2/; # jq.h use enum qw/:JQ_DEBUG_=1 TRACE TRACE_DETAIL TRACE_ALL/; use XSLoader; XSLoader::load('JSON::JQ', $VERSION); sub new { my ( $pkg, $param ) = @_; croak "script or script_file parameter required" unless exists $param->{script} or exists $param->{script_file}; my $self = {}; # script string or script file $self->{script} = $param->{script} if exists $param->{script}; $self->{script_file} = $param->{script_file} if exists $param->{script_file}; # script initial arguments $self->{variable} = exists $param->{variable} ? $param->{variable} : {}; # internal attributes $self->{_attribute}->{JQ_ORIGIN} = path($FindBin::Bin)->realpath->stringify if $FindBin::Bin; $self->{_attribute}->{JQ_LIBRARY_PATH} = exists $param->{library_paths} ? $param->{library_paths} : [ '~/.jq', '$ORIGIN/../lib/jq', '$ORIGIN/lib' ]; $self->{_attribute}->{PROGRAM_ORIGIN} = exists $param->{script_file} ? path($param->{script_file})->parent->stringify : '.'; # error callback will push error messages into this array $self->{_errors} = []; # debug callback print flags my $dump_opts = JV_PRINT_INDENT_FLAGS(2); $dump_opts |= JV_PRINT_SORTED; $dump_opts |= JV_PRINT_COLOR | JV_PRINT_ISATTY if isatty(*STDERR); $self->{_dumpopts} = $dump_opts; # jq debug flags $self->{jq_flags} = exists $param->{debug_flag} ? $param->{debug_flag} : 0; bless $self, $pkg; unless ($self->_init()) { croak "jq_compile_args() failed with errors:\n ". join("\n ", @{ $self->{_errors} }); } return $self; } sub process { my ( $self, $param ) = @_; my $input; if (exists $param->{data}) { $input = $param->{data}; } elsif (exists $param->{json}) { $input = from_json($param->{json}, { utf8 => 1 }); } elsif (exists $param->{json_file}) { my $file = path($param->{json_file}); $input = from_json($file->slurp, { utf8 => 1 }); } else { croak "JSON::JQ::process(): required parameter not found, check method documentation"; } my $output = []; my $rc = $self->_process($input, $output); # treat it as option EXIT_STATUS is on $rc -= 10 if $rc >= 10; if ($rc == 1) { # NOTE: treat this case as successful run warn "JSON::JQ::process(): returned null/false (undef output), perhaps the input is undef.\n"; } elsif ($rc == 4 and @{ $self->{_errors} } == 0) { # treat it as succeeded push @$output, undef; } elsif ($rc != 0) { croak "JSON::JQ::process(): failed with return code = $rc and errors:\n ". join("\n ", @{ $self->{_errors} }); } return wantarray ? @$output : $output; } =head1 NAME JSON::JQ - jq (https://stedolan.github.io/jq/) library binding =head1 SYNOPSIS use JSON::JQ; my $jq = JSON::JQ->new({ script => '.' }); # 1. process perl data my $results = $jq->process({ data => { foo => 'bar' }}); # 2. process json string my $results = $jq->process({ json => '{ "foo": "bar" }'}); # 3. process json file my $results = $jq->process({ json_file => 'foo.json' }); # check items in @$results =head1 DESCRIPTION This is a L library binding, making it possible to process data using jq script/filter/module. Check the jq homepage for a detailed explanation and documentation. =head1 METHODS =head2 new({ parameter => value, ... }) Construct a jq engine instance and return it, jq script must be provided by either I