diff --git a/README.md b/README.md index 20c83cd..fcdd251 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ expr = ( 'not ' | '! ' )? (value | comp | in_array) comp = value ('==' | '!=' | '<' | '>' | '<=' | '>=' | '=~') value value = (jsonpath | childpath | number | string | boolean | regpattern | null | length) length = (jsonpath | childpath) '.length' -in_array = value 'in' '[' value (',' value)* ']' +in_array = value 'in' ( '[' value (',' value)* ']' | jsonpath ) ``` ยน`var_name`: the regex roughly translates to "any valid JavaScript variable @@ -220,6 +220,7 @@ JsonPath | Result `$['store']` | The store. `$..book[*][title, 'category', "author"]` | title, category and author of all books. `$..book[?(@.author in [$.authors[0], $.authors[2]])]` | All books by "Nigel Rees" or "Herman Melville". +`$..book[?(@.author in $.authors)]` | All books written by an author in the authors list. `$.store.book[?(@.category == 'fiction' and @.price < 10 or @.color == "red")].price` | Books of fiction with a price lower than 10 or something with of color red. (`and` takes precedence to `or`) See more examples in the `./tests/Galbar/JsonPath` folder. diff --git a/src/Galbar/JsonPath/Expression/BooleanExpression.php b/src/Galbar/JsonPath/Expression/BooleanExpression.php index a02f5d8..af9a8c4 100644 --- a/src/Galbar/JsonPath/Expression/BooleanExpression.php +++ b/src/Galbar/JsonPath/Expression/BooleanExpression.php @@ -47,7 +47,7 @@ private static function booleanExpressionAnds(&$root, &$partial, $expression) if (preg_match(Language\Regex::BINOP_COMP, $subexpr, $match)) { $result = Comparison::evaluate($root, $partial, $match[1], $match[2], $match[3]); } elseif (preg_match(Language\Regex::BINOP_IN_ARRAY, $subexpr, $match)) { - $result = InArray::evaluate($root, $partial, $match[1], $match[2]); + $result = InArray::evaluate($root, $partial, $match[1], end($match)); } else { $result = Value::evaluate($root, $partial, $subexpr); } diff --git a/src/Galbar/JsonPath/Expression/InArray.php b/src/Galbar/JsonPath/Expression/InArray.php index 85be597..c7301a3 100644 --- a/src/Galbar/JsonPath/Expression/InArray.php +++ b/src/Galbar/JsonPath/Expression/InArray.php @@ -2,6 +2,8 @@ namespace JsonPath\Expression; +use JsonPath\Language; + class InArray { const SEPARATOR = ','; @@ -17,13 +19,38 @@ public static function evaluate(&$root, &$partial, $attribute, $listExpression) private static function prepareList(&$root, &$partial, $expression) { if (strpos($expression, self::SEPARATOR) === false) { - return [Value::evaluate($root, $partial, trim($expression))]; + if ($expression[0] === Language\Token::ROOT){ + list($result, $_) = \JsonPath\JsonPath::subtreeGet($root, $root, $expression); + if (!$_) { + $result = reset($result); + } + return $result; + } + else if ($expression[0] === Language\Token::CHILD) { + $expression[0] = Language\Token::ROOT; + list($result, $_) = \JsonPath\JsonPath::subtreeGet($root, $partial, $expression); + if (!$_) { + $result = reset($result); + } + return $result; + } + + return [Value::evaluate($root, $partial, self::unwrapExpression($expression))]; } return array_map( function ($value) use ($root, $partial) { return Value::evaluate($root, $partial, trim($value)); }, - explode(self::SEPARATOR, $expression) + explode(self::SEPARATOR, self::unwrapExpression($expression)) ); } + + private static function unwrapExpression($expression) { + if ($expression[0] === Language\Token::SELECTOR_BEGIN + && $expression[strlen($expression) - 1] === Language\Token::SELECTOR_END + ) { + $expression = substr($expression, 1, -1); + } + return trim($expression); + } } diff --git a/src/Galbar/JsonPath/Language/Regex.php b/src/Galbar/JsonPath/Language/Regex.php index c574391..ef4f9ee 100644 --- a/src/Galbar/JsonPath/Language/Regex.php +++ b/src/Galbar/JsonPath/Language/Regex.php @@ -38,7 +38,7 @@ class Regex const EXPR_STRING = '/^(?:\'(.*)\'|"(.*)")$/'; const EXPR_REGEX = '/^\/.*\/i?x?$/'; const BINOP_COMP = '/^(.+)\s*(==|!=|<=|>=|<|>|=\~)\s*(.+)$/'; - const BINOP_IN_ARRAY = '/^(.+)\s*in\s*\[(.+)\]$/'; + const BINOP_IN_ARRAY = '/^(.+)\s*in\s*(\[.+\]|[\$|@]\..+)$/'; const BINOP_OR = '/\s+(or|\|\|)\s+/'; const BINOP_AND = '/\s+(and|&&)\s+/'; const OP_NOT = '/^(not|!)\s+(.*)/'; diff --git a/tests/Galbar/JsonPath/JsonObjectTest.php b/tests/Galbar/JsonPath/JsonObjectTest.php index 92abd29..6e87a9e 100644 --- a/tests/Galbar/JsonPath/JsonObjectTest.php +++ b/tests/Galbar/JsonPath/JsonObjectTest.php @@ -896,6 +896,53 @@ public function testSmartGetProvider() ) ), "$..book[?(@.author in [$.authors[0], $.authors[2]])]" + ), + array( + array( + array( + "category" => "reference", + "author" => "Nigel Rees", + "title" => "Sayings of the Century", + "price" => 8.95, + "available" => true, + ) + ), + '$..book[?(@.author in [$.authors[*]])]' + ), + array( + array ( + array ( + 'category' => 'reference', + 'author' => 'Nigel Rees', + 'title' => 'Sayings of the Century', + 'price' => 8.95, + 'available' => true, + ), + array ( + 'category' => 'fiction', + 'author' => 'Evelyn Waugh', + 'title' => 'Sword of Honour', + 'price' => 12.99, + 'available' => false, + ), + array ( + 'category' => 'fiction', + 'author' => 'Herman Melville', + 'title' => 'Moby Dick', + 'isbn' => '0-553-21311-3', + 'price' => 8.99, + 'available' => true, + ), + array ( + 'category' => 'fiction', + 'author' => 'J. R. R. Tolkien', + 'title' => 'The Lord of the Rings', + 'isbn' => '0-395-19395-8', + 'price' => 22.99, + 'available' => false, + ), + ), + '$..book[?(@.author in $.authors[*])]' ) ); }