Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/alpine
{
"name": "Alpine",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/base:alpine-3.22",
"features": {
"ghcr.io/haukex/devcontainer-features/perl:1": {}
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "cpanm --installdeps ."
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot

version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
30 changes: 10 additions & 20 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,23 @@ on:
pull_request:
branches:
- master

push:
branches:
- master

jobs:
build:
name: ${{ matrix.perl }}
runs-on: ubuntu-latest

runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
perl: [
'5.10', '5.12', '5.14', '5.16', '5.18', '5.20', '5.22', '5.24',
'5.26', '5.28', '5.30', '5.32', '5.34'
]

os: ["ubuntu-latest", "macos-latest", "windows-latest"]
perl: ["5.38", "5.36", "5.34"]
name: Perl ${{ matrix.perl }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v2

- uses: shogo82148/actions-setup-perl@v1
- uses: actions/checkout@v6
- name: Set up perl
uses: shogo82148/actions-setup-perl@v1
with:
perl-version: ${{ matrix.perl }}

- run: perl -v

- run: cpanm --cpanfile cpanfile --installdeps --notest .

- run: prove -l
install-modules-with: cpanm
install-modules-args: --with-develop --with-configure
- run: prove -lv t
41 changes: 41 additions & 0 deletions .github/workflows/workflow-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Workflow test
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
perl-versions:
runs-on: ubuntu-latest
name: List Perl versions
outputs:
perl-versions: ${{ steps.action.outputs.perl-versions }}
steps:
- id: action
uses: perl-actions/perl-versions@v1
with:
since-perl: v5.10
with-devel: true

perl_tester:
runs-on: ubuntu-latest
name: "Perl ${{ matrix.perl-version }}"
needs: [perl-versions]

strategy:
fail-fast: false
matrix:
perl-version: ${{ fromJson (needs.perl-versions.outputs.perl-versions) }}

container: perldocker/perl-tester:${{ matrix.perl-version }}

steps:
- uses: actions/checkout@v6
- name: uses install-with-cpanm
uses: perl-actions/install-with-cpanm@v1
with:
cpanfile: "cpanfile"
sudo: false
- run: prove -lvr t
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"perlnavigator.includePaths": [
"/home/vscode/perl5/lib",
"/home/vscode/perl5/lib/perl5"
]
}
4 changes: 3 additions & 1 deletion cpanfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
requires 'Carp::Assert';
requires 'Exporter::Tiny';
requires 'Exporter::Shiny';
requires 'JSON::MaybeXS';
requires 'LV';
requires 'List::Util' => '1.45'; # For uniq.
requires 'Readonly';
requires 'Try::Tiny';
requires 'Class::Accessor::Fast';
requires 'perl' => '5.010';

on test => sub {
requires 'Test2::V0';
requires 'Test2::V1';
};
2 changes: 1 addition & 1 deletion dist.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ copyright_holder = Aurelia Peters
copyright_year = 2024

; authordep Pod::Elemental::Transformer::List
version = 1.0.5
version = 1.0.6
[Prereqs::FromCPANfile]
[@Basic]
[MetaJSON]
Expand Down
12 changes: 6 additions & 6 deletions lib/JSON/Path.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ use LV ();
use overload '""' => \&to_string;

sub jpath {
my ( $object, $expression ) = @_;
my @return = __PACKAGE__->new($expression)->values($object);
my ( $object, $expression, %args ) = @_;
return __PACKAGE__->new($expression)->values($object, %args);
}

sub jpath1 : lvalue {
my ( $object, $expression ) = @_;
__PACKAGE__->new($expression)->value($object);
return __PACKAGE__->new($expression)->value($object);
}

sub jpath_map (&$$) {
Expand Down Expand Up @@ -97,10 +97,10 @@ sub value : lvalue {
}

sub values {
my ( $self, $object ) = @_;
my ( $self, $object, %args ) = @_;
croak q{non-safe evaluation, died} if "$self" =~ /\?\(/ && $JSON::Path::Safe;

return JSON::Path::Evaluator::evaluate_jsonpath( $object, "$self", script_engine => 'perl' );
return JSON::Path::Evaluator::evaluate_jsonpath( $object, "$self", %args, script_engine => 'perl' );
}

sub map {
Expand Down Expand Up @@ -391,7 +391,7 @@ Copyright 2007 Stefan Goessner.

Copyright 2010-2013 Toby Inkster.

Copyright 2021-2024 Aurelia Peters
Copyright 2021-2026 Aurelia Peters

This module is tri-licensed. It is available under the X11 (a.k.a. MIT)
licence; you can also redistribute it and/or modify it under the same
Expand Down
16 changes: 10 additions & 6 deletions lib/JSON/Path/Evaluator.pm
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ sub evaluate_jsonpath {
_calling_context => wantarray ? 'ARRAY' : 'SCALAR',
%args
);
return $self->evaluate( $expression, want_ref => $want_ref, want_path => $want_path );
return $self->evaluate( $expression, want_ref => $want_ref, want_path => $want_path, debug => $args{'debug'} );
}

=method evaluate
Expand Down Expand Up @@ -189,9 +189,8 @@ sub evaluate {

my $json_object = $self->{root};

my $token_stream = [ tokenize($expression) ];
#use Data::Dumper qw/Dumper/;
#print Dumper $token_stream;
my $token_stream = [ tokenize($expression, debug => $args{'debug'}) ];

shift @{$token_stream} if $token_stream->[0] eq $TOKEN_ROOT;
shift @{$token_stream} if $token_stream->[0] eq $TOKEN_CHILD;

Expand All @@ -208,7 +207,7 @@ sub evaluate {
}

my @ret = $self->_evaluate( $json_object, $token_stream, $args{want_ref} );
return @ret;
return wantarray ? @ret : $ret[0];
}

sub _reftable_walker {
Expand Down Expand Up @@ -362,7 +361,12 @@ sub _get {
}
else {
my @indices;
if ( $index =~ /$TOKEN_ARRAY_SLICE/ ) {
# Only handle slices when all elements of the index save the slice
# operator are numeric. THIS IS A KLUDGE. By rights I should know
# that $index is a string ('foo:bar') instead of a slice operation
# (1:2 or 1:2:3) I'll fix it properly when I rework paths as a
# series of anonymous subs. -- 2026-02-14 popefelix@gmail.com
if ( $index =~ /$TOKEN_ARRAY_SLICE/ && ( ! grep { $_ && ! looks_like_number($_) } split /:/, $index)) {
my $length = _hashlike($object) ? scalar values %{$object} : scalar @{$object};
@indices = _slice( $index, $length );
}
Expand Down
27 changes: 17 additions & 10 deletions lib/JSON/Path/Tokenizer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Readonly my %OPERATORS => (
# Take an expression and break it up into tokens
sub tokenize {
my $expression = shift;
my %args = @_;

#print "Tokenize \"$expression\"\n";
my $chars = [ split //, $expression ];

Expand All @@ -42,6 +44,9 @@ sub tokenize {
push @tokens, _read_to_filter_script_close($chars);
}
}
if ( $args{'debug'} ) {
print STDERR qq([DEBUG] Tokens: ) . join( ', ', map { qq("$_") } @tokens ) . qq(\n);
}
return @tokens;
}

Expand All @@ -61,20 +66,20 @@ sub _read_to_filter_script_close {
"\\" => "\x{005C}",
);

#print "$invocation: read to filter script close: " . join( '', @{$chars} ) . "\n";
#print "$invocation: read to filter script close: " . join( '', @{$chars} ) . "\n";
my $filter;
my $in_regex;
my $in_quote;
my $escape = 0;

my @quote_chars = ($APOSTROPHE, $QUOTATION_MARK);
my @quote_chars = ( $APOSTROPHE, $QUOTATION_MARK );
my @regex_chars = "/";

while ( defined( my $char = shift @{$chars} ) ) {
if ( $in_quote ) {
if ( $escape ) {
if ($in_quote) {
if ($escape) {
## Replace \t by tab, \\ by \, etc
$char = $escaped_chars{$char} || $char;
$char = $escaped_chars{$char} || $char;
$escape = 0;
}
elsif ( $char eq "\\" ) {
Expand All @@ -87,8 +92,8 @@ sub _read_to_filter_script_close {
$in_quote = '';
}
}
elsif ( $in_regex ) {
if ( $escape ) {
elsif ($in_regex) {
if ($escape) {
$escape = 0;
}
elsif ( $char eq "\\" ) {
Expand All @@ -98,10 +103,10 @@ sub _read_to_filter_script_close {
$in_regex = '';
}
}
elsif (grep { $_ eq $char } @quote_chars) {
elsif ( grep { $_ eq $char } @quote_chars ) {
$in_quote = $char;
}
elsif (grep { $_ eq $char } @regex_chars) {
elsif ( grep { $_ eq $char } @regex_chars ) {
$in_regex = $char;
}

Expand All @@ -114,6 +119,7 @@ sub _read_to_filter_script_close {
}

sub _read_to_next_token {

#$invocation++;
my $chars = shift;

Expand All @@ -123,7 +129,8 @@ sub _read_to_next_token {
while ( defined( my $char = shift @{$chars} ) ) {

if ( $char eq $APOSTROPHE || $char eq $QUOTATION_MARK ) {
#print "$invocation: Char is $APOSTROPHE or $QUOTATION_MARK. Char: $char, in_quote: $in_quote\n";

#print "$invocation: Char is $APOSTROPHE or $QUOTATION_MARK. Char: $char, in_quote: $in_quote\n";
if ( $in_quote && $in_quote eq $char ) {
$in_quote = '';
last;
Expand Down
45 changes: 45 additions & 0 deletions t/colon-in-key.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use Test2::V1 -ipP;
use JSON::Path qw/jpath/;
use JSON::MaybeXS qw/decode_json/;
use Test2::Tools::Exception qw/lives try_ok/;

subtest simple => sub {
my $json_string = q(
{
"key:one": "value1",
"key:2": "value2",
":keythree": "value3"
}
);
my $data_ref = decode_json($json_string);
for my $key ( keys %{$data_ref} ) {
my $expr = sprintf q($['%s']), $key;
try_ok {
my $expected = $data_ref->{$key};
my ($got) = jpath( $data_ref, $expr, debug => $ENV{'DEBUG_TOKENIZER'} );
is($got, $expected, qq("$expr") );
};
}
};

subtest 'not so simple' => sub {
my $json_string = q(
{
"key1": [
{
":key2:test": {
"parameters": "desired_value"
}
}
]
}
);

my $data_ref = decode_json($json_string);
my $expr = q($['key1'][0][':key2:test']);
my $got = jpath($data_ref, $expr);
my $expected = $data_ref->{'key1'}[0]{':key2:test'};
is($got, $expected, qq("$expr"));
done_testing;
};
done_testing;
3 changes: 3 additions & 0 deletions t/evaluator/tokenize.t
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use Test2::V0;
use JSON::Path::Tokenizer qw(tokenize);
use Data::Dumper;

my @EXPRESSIONS = (
q{$[1:3:2].foobar} => [qw/$ [ 1:3:2 ] . foobar/],
Expand All @@ -20,6 +21,8 @@ my @EXPRESSIONS = (
q{$.[*].user[?(@.login == 'laurilehmijoki')]} => [ qw/$ . [ * ] . user [?(/, q{@.login == 'laurilehmijoki'}, ')]' ],
q{$.path\.one.two} => [qw/$ . path.one . two/],
q{$.'path.one'.two} => [qw/$ . path.one . two/],
q{$['foo:bar']} => ['$', '[', 'foo:bar', ']'],
q($['key1'][0][':key2:test']) => ['$', '[', 'key1', ']', '[', '0', ']', '[', ':key2:test', ']',]
);

for ( 0 .. ( $#EXPRESSIONS / 2 ) ) {
Expand Down
Loading