Giter Site home page Giter Site logo

jsonpath's Introduction

JsonPath

This is an implementation of http://goessner.net/articles/JsonPath/.

What is JsonPath?

JsonPath is a way of addressing elements within a JSON object. Similar to xpath of yore, JsonPath lets you traverse a json object and manipulate or access it.

Usage

Command-line

There is stand-alone usage through the binary jsonpath

jsonpath [expression] (file|string)

If you omit the second argument, it will read stdin, assuming one valid JSON
object per line. Expression must be a valid jsonpath expression.

Library

To use JsonPath as a library simply include and get goin'!

require 'jsonpath'

json = <<-HERE_DOC
{"store":
  {"bicycle":
    {"price":19.95, "color":"red"},
    "book":[
      {"price":8.95, "category":"reference", "title":"Sayings of the Century", "author":"Nigel Rees"},
      {"price":12.99, "category":"fiction", "title":"Sword of Honour", "author":"Evelyn Waugh"},
      {"price":8.99, "category":"fiction", "isbn":"0-553-21311-3", "title":"Moby Dick", "author":"Herman Melville","color":"blue"},
      {"price":22.99, "category":"fiction", "isbn":"0-395-19395-8", "title":"The Lord of the Rings", "author":"Tolkien"}
    ]
  }
}
HERE_DOC

Now that we have a JSON object, let's get all the prices present in the object. We create an object for the path in the following way.

path = JsonPath.new('$..price')

Now that we have a path, let's apply it to the object above.

path.on(json)
# => [19.95, 8.95, 12.99, 8.99, 22.99]

Or reuse it later on some other object (thread safe) ...

path.on('{"books":[{"title":"A Tale of Two Somethings","price":18.88}]}')
# => [18.88]

You can also just combine this into one mega-call with the convenient JsonPath.on method.

JsonPath.on(json, '$..author')
# => ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "Tolkien"]

Of course the full JsonPath syntax is supported, such as array slices

JsonPath.new('$..book[::2]').on(json)
# => [
#      {"price" => 8.95, "category" => "reference", "title" => "Sayings of the Century", "author" => "Nigel Rees"},
#      {"price" => 8.99, "category" => "fiction", "isbn" => "0-553-21311-3", "title" => "Moby Dick", "author" => "Herman Melville","color" => "blue"},
#    ]

...and evals, including those with conditional operators

JsonPath.new("$..price[?(@ < 10)]").on(json)
# => [8.95, 8.99]

JsonPath.new("$..book[?(@['price'] == 8.95 || @['price'] == 8.99)].title").on(json)
# => ["Sayings of the Century", "Moby Dick"]

JsonPath.new("$..book[?(@['price'] == 8.95 && @['price'] == 8.99)].title").on(json)
# => []

There is a convenience method, #first that gives you the first element for a JSON object and path.

JsonPath.new('$..color').first(json)
# => "red"

As well, we can directly create an Enumerable at any time using #[].

enum = JsonPath.new('$..color')[json]
# => #<JsonPath::Enumerable:...>
enum.first
# => "red"
enum.any?{ |c| c == 'red' }
# => true

For more usage examples and variations on paths, please visit the tests. There are some more complex ones as well.

Querying ruby data structures

If you have ruby hashes with symbolized keys as input, you can use :use_symbols to make JsonPath work fine on them too:

book = { title: "Sayings of the Century" }

JsonPath.new('$.title').on(book)
# => []

JsonPath.new('$.title', use_symbols: true).on(book)
# => ["Sayings of the Century"]

JsonPath also recognizes objects responding to dig (introduced in ruby 2.3), and therefore works out of the box with Struct, OpenStruct, and other Hash-like structures:

book_class = Struct.new(:title)
book = book_class.new("Sayings of the Century")

JsonPath.new('$.title').on(book)
# => ["Sayings of the Century"]

JsonPath is able to query pure ruby objects and uses __send__ on them. The option is enabled by default in JsonPath 1.x, but we encourage to enable it explicitly:

book_class = Class.new{ attr_accessor :title }
book = book_class.new
book.title = "Sayings of the Century"

JsonPath.new('$.title', allow_send: true).on(book)
# => ["Sayings of the Century"]

Other available options

By default, JsonPath does not return null values on unexisting paths. This can be changed using the :default_path_leaf_to_null option

JsonPath.new('$..book[*].isbn').on(json)
# => ["0-553-21311-3", "0-395-19395-8"]

JsonPath.new('$..book[*].isbn', default_path_leaf_to_null: true).on(json)
# => [nil, nil, "0-553-21311-3", "0-395-19395-8"]

When JsonPath returns a Hash, you can ask to symbolize its keys using the :symbolize_keys option

JsonPath.new('$..book[0]').on(json)
# => [{"category" => "reference", ...}]

JsonPath.new('$..book[0]', symbolize_keys: true).on(json)
# => [{category: "reference", ...}]

Selecting Values

It's possible to select results once a query has been defined after the query. For example given this JSON data:

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99
            }
        ]
}

... and this query:

"$.store.book[*](category,author)"

... the result can be filtered as such:

[
   {
      "category" : "reference",
      "author" : "Nigel Rees"
   },
   {
      "category" : "fiction",
      "author" : "Evelyn Waugh"
   }
]

Manipulation

If you'd like to do substitution in a json object, you can use #gsub or #gsub! to modify the object in place.

JsonPath.for('{"candy":"lollipop"}').gsub('$..candy') {|v| "big turks" }.to_hash

The result will be

{'candy' => 'big turks'}

If you'd like to remove all nil keys, you can use #compact and #compact!. To remove all keys under a certain path, use #delete or #delete!. You can even chain these methods together as follows:

json = '{"candy":"lollipop","noncandy":null,"other":"things"}'
o = JsonPath.for(json).
  gsub('$..candy') {|v| "big turks" }.
  compact.
  delete('$..other').
  to_hash
# => {"candy" => "big turks"}

Fetch all paths

To fetch all possible paths in given json, you can use `fetch_all_path`` method.

data:

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees"
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh"
            }
        ]
}

... and this query:

JsonPath.fetch_all_path(data)

... the result will be:

["$", "$.store", "$.store.book", "$.store.book[0].category", "$.store.book[0].author", "$.store.book[0]", "$.store.book[1].category", "$.store.book[1].author", "$.store.book[1]"]

Contributions

Please feel free to submit an Issue or a Pull Request any time you feel like you would like to contribute. Thank you!

Running an individual test

ruby -Ilib:../lib test/test_jsonpath.rb --name test_wildcard_on_intermediary_element_v6

jsonpath's People

Contributors

a5-stable avatar anupama-kumari avatar anuravi98 avatar bk-chovatiya avatar blambeau avatar blatyo avatar carld avatar doits avatar gogainda avatar gongfarmer avatar grosser avatar ipost avatar jgoodw1n avatar jhigman avatar johnallen3d avatar johnlauck avatar joshbuddy avatar khairihafsham avatar knu avatar maniodev avatar metade avatar michaelkruglos avatar mohana-rengasamy avatar mohanapriya2308 avatar notethan avatar opsb avatar seikitsu avatar shepfc3 avatar skarlso avatar stve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsonpath's Issues

Cannot filter by digits-only string

require "jsonpath"

json = <<'EOF'
{
  "foo": {
    "bar": "123",
    "baz": "abc"
  }
}
EOF

# OK
# should be: [{"bar"=>"123", "baz"=>"abc"}]
jsonpath = "$.foo[?(@.baz=='abc')]"
path = JsonPath.new(jsonpath)
p path.on(json)

# NG
# should be: [{"bar"=>"123", "baz"=>"abc"}]
# got: []
jsonpath = "$.foo[?(@.bar=='123')]"
path = JsonPath.new(jsonpath)
p path.on(json)

Infinite loop on parsing expression

in version 5.8.0 this expression worked:
$.acceptNewTasks.[?(@.taskEndpoint == "mps/awesome")].lastTaskPollTime

Now there is an infinite loop in the Parser.parse_exp function in versions 7.0.0 and up.

Ping @Voomer.

New Collaborator

Hey folks.

I've been added as a collaborator, so I'm going to address all the issues one-by-one and apply some refactorings I already did.

Please feel free to submit new issues if you have any desires.

Cheers,
Gergely.

example incorrect

Current readme example

JsonPath.new('$..price[?(@ < 20)]').on(json)
# => [8.95, 8.99]

should be:

JsonPath.new('$..price[?(@ < 10)]').on(json)
# => [8.95, 8.99]

Support '@' in jsonpath filter

I'd like to filter json+ld data so I should include @ in jsonpath
i.e. $.mentions[?(@['@type'] == 'Place')] I got empty result although my json+ld data has a @type == Place
the data format is:

jsonld = {
   "mentions":[
      {
         "name":"Delimara Powerplant",
         "identifier":"krzana://took/powerstation/Delimara Powerplant",
         "@type":"Place",
         "geo":{
            "latitude":35.83020073454,
            "longitude":14.55602645874
         }
      }
   ]
}

the full jsonpath is JsonPath.new("$.mentions[?(@['@type'] == 'Place')]").on(jsonld)

The @ symbol could not be a child json object

How can I get value parameter of an item in chilren list using the code parameter ?

I found only one way to do this and I dont like it:

$..children.[?(@.info.code == 'code1')]..value

I dont like that the filter expression does not iterate recursively using double dots like

$..children.[?(@..code == 'code1')]..value -- this sentence gives me an error.

Is there any way to get value using shorter expression and in more elegant way?
it's only example and real world parameter names can be way longer. and the structure itself may be deeper so I need to write shorter queries.

{
    "object": {
        "children": [
            {
                "info": {
                    "id": "1",
                    "code": "code1"
                },
                "values": [
                    {
                        "value": "test"
                    }
                ]
            },
            {
                "info": {
                    "id": "2",
                    "code": "code2"
                },
                "values": [
                    {
                        "value": true
                    }
                ]
            }
        ]
    }
}

Delete elements from array using filter instead of index

Hi @Skarlso, I don't think this is an issue but maybe a feature request actually. I have been working a lot with this project, and at some point, I found myself trying to delete elements from an array that matches a condition.

Something like this:

Input JSON

{ 'store' => {
      'book' => [
        { 'category' => 'reference',
          'author' => 'Nigel Rees',
          'title' => 'Sayings of the Century',
          'price' => 9,
          'tags' => %w[asdf asdf2] },
        { 'category' => 'fiction',
          'author' => 'Evelyn Waugh',
          'title' => 'Sword of Honour',
          'price' => 13 }
      ]
} }

JSONPath

JsonPath.for(@object).delete!("$.store.book[?(@.category == 'reference')]")

Expected output:

{ 'store' => {
      'book' => [
        { 'category' => 'fiction',
          'author' => 'Evelyn Waugh',
          'title' => 'Sword of Honour',
          'price' => 13 }
      ]
} }

However, it's returning the original object unmodified. The weird thing is that it works for search or even for replace.

JsonPath.for(@object).gsub!("$.store.book[?(@.category == 'reference')]") { |_| {} }

Output:

{ 'store' => {
      'book' => [
        {},
        { 'category' => 'fiction',
          'author' => 'Evelyn Waugh',
          'title' => 'Sword of Honour',
          'price' => 13 }
      ]
} }

Is this expected to work, or there's no logic to handle that? I searched on the tests file, but couldn't find a test case for this kind of deletion.

Filters don't work on inconsistent children

Following example produces exception:

data=<<-EOD { "store": { "book": [ "string_instead_of_object", { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } } EOD JsonPath.new("$..book[?(@.price > 20)]").on(data)

Support for paths with hyphens

  def example_object
    { "store"=> {
        "bicycle"=> {
            "color"=> "red",
            "price"=> 20,
            "catalogue_number" => 12345,
            "2seater" => "yes",
            "number-one" => 1}
    } }
  end

p JsonPath.new('$.store.bicycle.catalogue_number').on(example_object) # => [12345]
p JsonPath.new('$.store.bicycle.number-one').on(example_object) # => []

When child array index doesn't exist, still get return value

This test fails:

def test_lookup_missing_element
assert_equal [], JsonPath.new('$.store.book[99].price').on(@object)
end

  1. Failure:
    test_lookup_missing_element(TestJsonpath) [/home/jzh/git/jsonpath/test/test_jsonpath.rb:13]:
    Expected: []
    Actual: [23]

I would expect to get an empty response, or possibly exception, if a child element is not found.

Instead, it seems like the search for the 99th element keeps looping through the actual elements, and returns the price from the element last parsed?

Something changed from 0.8.11 to 0.9.0

We are using jsonpath in a cucumber testing setup. Locally, I installed gem version 0.8.11. Our Jenkins server originally also had 0.8.11. We recently started using Jenkins slightly differently, using docker container rather than static Amazon AMI, and that container did not specify which version of jsonpath gem, and ended up getting 0.9.0. Tests that work with 0.8.11 are failing with 0.9.0. I also ran the test locally with 0.8.11 and with 0.9.0 and got the same results, so I am fairly certain that something changed.

In 0.8.11, the following jsonpath query and file return "[true]", but with 0.9.0 I am getting "[]"

Query:
$..column[?(@.name=='ASSOCIATE_FLAG')].nullable

{
  "snapshot": {
    "created": "2018-06-05T00:50:39.918",
    "database": {
      "productVersion": "Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production",
      "shortName": "oracle",
      "majorVersion": "12",
      "minorVersion": "2",
      "user": "DDBCLI",
      "productName": "Oracle",
      "url": "jdbc:oracle:thin:@//10.158.178.236:1521/pdborcl"
    },
    "objects": {
      "liquibase.structure.core.Catalog": [
        {
          "catalog": {
            "default": true,
            "name": "DDBCLI",
            "snapshotId": "0412100"
          }
        }]
      ,
      "liquibase.structure.core.Column": [
        {
          "column": {
            "name": "ASSOCIATE_FLAG",
            "nullable": true,
            "order": "4!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412117",
            "snapshotId": "0412124",
            "type": {
              "columnSize": "1!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            },
            "validateNullable": false
          }
        },
        {
          "column": {
            "name": "AUTHOR",
            "nullable": false,
            "order": "2!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412104",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN1",
            "nullable": false,
            "order": "1!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412117",
            "snapshotId": "0412122",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN11",
            "nullable": false,
            "order": "1!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412133",
            "snapshotId": "0412135",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN1_REF",
            "nullable": true,
            "order": "2!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412133",
            "snapshotId": "0412136",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN2",
            "nullable": true,
            "order": "2!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412117",
            "snapshotId": "0412119",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN3",
            "nullable": false,
            "order": "3!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412117",
            "snapshotId": "0412123",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COLUMN33",
            "nullable": true,
            "order": "3!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412133",
            "snapshotId": "0412137",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "COMMENTS",
            "nullable": true,
            "order": "9!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412111",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "CONTEXTS",
            "nullable": true,
            "order": "12!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412114",
            "type": {
              "columnSize": "4000!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "DATEEXECUTED",
            "nullable": false,
            "order": "4!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412106",
            "type": {
              "dataTypeId": "93!{java.lang.Integer}",
              "typeName": "TIMESTAMP(6)"
            }
          }
        },
        {
          "column": {
            "name": "DEPLOYMENT_ID",
            "nullable": true,
            "order": "14!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412116",
            "type": {
              "columnSize": "10!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "DESCRIPTION",
            "nullable": true,
            "order": "8!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412110",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "EXECTYPE",
            "nullable": false,
            "order": "6!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412108",
            "type": {
              "columnSize": "10!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "FILENAME",
            "nullable": false,
            "order": "3!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412105",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "ID",
            "nullable": false,
            "order": "1!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412103",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "ID",
            "nullable": false,
            "order": "1!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412126",
            "snapshotId": "0412128",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "LABELS",
            "nullable": true,
            "order": "13!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412115",
            "type": {
              "columnSize": "4000!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "LIQUIBASE",
            "nullable": true,
            "order": "11!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412113",
            "type": {
              "columnSize": "20!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "LOCKED",
            "nullable": false,
            "order": "2!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412126",
            "snapshotId": "0412129",
            "type": {
              "columnSize": "1!{java.lang.Integer}",
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "LOCKEDBY",
            "nullable": true,
            "order": "4!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412126",
            "snapshotId": "0412131",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "LOCKGRANTED",
            "nullable": true,
            "order": "3!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412126",
            "snapshotId": "0412130",
            "type": {
              "dataTypeId": "93!{java.lang.Integer}",
              "typeName": "TIMESTAMP(6)"
            }
          }
        },
        {
          "column": {
            "name": "MD5SUM",
            "nullable": true,
            "order": "7!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412109",
            "type": {
              "columnSize": "35!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        },
        {
          "column": {
            "name": "ORDEREXECUTED",
            "nullable": false,
            "order": "5!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412107",
            "type": {
              "dataTypeId": "3!{java.lang.Integer}",
              "decimalDigits": "0!{java.lang.Integer}",
              "typeName": "NUMBER"
            }
          }
        },
        {
          "column": {
            "name": "TAG",
            "nullable": true,
            "order": "10!{java.lang.Integer}",
            "relation": "liquibase.structure.core.Table#0412102",
            "snapshotId": "0412112",
            "type": {
              "columnSize": "255!{java.lang.Integer}",
              "columnSizeUnit": "BYTE!{liquibase.structure.core.DataType$ColumnSizeUnit}",
              "dataTypeId": "12!{java.lang.Integer}",
              "typeName": "VARCHAR"
            }
          }
        }]
      ,
      "liquibase.structure.core.ForeignKey": [
        {
          "foreignKey": {
            "deferrable": false,
            "deleteRule": "importedKeyRestrict!{liquibase.structure.core.ForeignKeyConstraintType}",
            "foreignKeyColumns": [
              "liquibase.structure.core.Column#0412136"]
            ,
            "foreignKeyTable": "liquibase.structure.core.Table#0412133",
            "initiallyDeferred": false,
            "name": "FK_COLUMN2_REF",
            "primaryKeyColumns": [
              "liquibase.structure.core.Column#0412122"]
            ,
            "primaryKeyTable": "liquibase.structure.core.Table#0412117",
            "snapshotId": "0412138",
            "updateRule": "importedKeyRestrict!{liquibase.structure.core.ForeignKeyConstraintType}",
            "validate": false
          }
        }]
      ,
      "liquibase.structure.core.Index": [
        {
          "index": {
            "columns": [
              "liquibase.structure.core.Column#0412128"]
            ,
            "name": "PK_DATABASECHANGELOGLOCK",
            "snapshotId": "0412127",
            "table": "liquibase.structure.core.Table#0412126",
            "unique": true
          }
        },
        {
          "index": {
            "columns": [
              "liquibase.structure.core.Column#0412122"]
            ,
            "name": "PK_TABLE1",
            "snapshotId": "0412121",
            "table": "liquibase.structure.core.Table#0412117",
            "unique": true
          }
        },
        {
          "index": {
            "columns": [
              "liquibase.structure.core.Column#0412135"]
            ,
            "name": "PK_TABLE2",
            "snapshotId": "0412134",
            "table": "liquibase.structure.core.Table#0412133",
            "unique": true
          }
        },
        {
          "index": {
            "columns": [
              "liquibase.structure.core.Column#0412119"]
            ,
            "name": "UC_TABLE1_COLUMN2",
            "snapshotId": "0412120",
            "table": "liquibase.structure.core.Table#0412117",
            "unique": true
          }
        }]
      ,
      "liquibase.structure.core.PrimaryKey": [
        {
          "primaryKey": {
            "backingIndex": "liquibase.structure.core.Index#0412127",
            "columns": [
              "liquibase.structure.core.Column#0412128"]
            ,
            "name": "PK_DATABASECHANGELOGLOCK",
            "snapshotId": "0412132",
            "table": "liquibase.structure.core.Table#0412126",
            "validate": true
          }
        },
        {
          "primaryKey": {
            "backingIndex": "liquibase.structure.core.Index#0412121",
            "columns": [
              "liquibase.structure.core.Column#0412122"]
            ,
            "name": "PK_TABLE1",
            "snapshotId": "0412125",
            "table": "liquibase.structure.core.Table#0412117",
            "validate": false
          }
        },
        {
          "primaryKey": {
            "backingIndex": "liquibase.structure.core.Index#0412134",
            "columns": [
              "liquibase.structure.core.Column#0412135"]
            ,
            "name": "PK_TABLE2",
            "snapshotId": "0412139",
            "table": "liquibase.structure.core.Table#0412133",
            "validate": true
          }
        }]
      ,
      "liquibase.structure.core.Schema": [
        {
          "schema": {
            "catalog": "liquibase.structure.core.Catalog#0412100",
            "default": true,
            "name": "DDBCLI",
            "snapshotId": "0412101"
          }
        }]
      ,
      "liquibase.structure.core.Table": [
        {
          "table": {
            "columns": [
              "liquibase.structure.core.Column#0412103",
              "liquibase.structure.core.Column#0412104",
              "liquibase.structure.core.Column#0412105",
              "liquibase.structure.core.Column#0412106",
              "liquibase.structure.core.Column#0412107",
              "liquibase.structure.core.Column#0412108",
              "liquibase.structure.core.Column#0412109",
              "liquibase.structure.core.Column#0412110",
              "liquibase.structure.core.Column#0412111",
              "liquibase.structure.core.Column#0412112",
              "liquibase.structure.core.Column#0412113",
              "liquibase.structure.core.Column#0412114",
              "liquibase.structure.core.Column#0412115",
              "liquibase.structure.core.Column#0412116"]
            ,
            "name": "DATABASECHANGELOG",
            "schema": "liquibase.structure.core.Schema#0412101",
            "snapshotId": "0412102"
          }
        },
        {
          "table": {
            "columns": [
              "liquibase.structure.core.Column#0412128",
              "liquibase.structure.core.Column#0412129",
              "liquibase.structure.core.Column#0412130",
              "liquibase.structure.core.Column#0412131"]
            ,
            "indexes": [
              "liquibase.structure.core.Index#0412127"]
            ,
            "name": "DATABASECHANGELOGLOCK",
            "primaryKey": "liquibase.structure.core.PrimaryKey#0412132",
            "schema": "liquibase.structure.core.Schema#0412101",
            "snapshotId": "0412126"
          }
        },
        {
          "table": {
            "columns": [
              "liquibase.structure.core.Column#0412122",
              "liquibase.structure.core.Column#0412119",
              "liquibase.structure.core.Column#0412123",
              "liquibase.structure.core.Column#0412124"]
            ,
            "indexes": [
              "liquibase.structure.core.Index#0412121",
              "liquibase.structure.core.Index#0412120"]
            ,
            "name": "TABLE1",
            "primaryKey": "liquibase.structure.core.PrimaryKey#0412125",
            "schema": "liquibase.structure.core.Schema#0412101",
            "snapshotId": "0412117",
            "uniqueConstraints": [
              "liquibase.structure.core.UniqueConstraint#0412118"]
            
          }
        },
        {
          "table": {
            "columns": [
              "liquibase.structure.core.Column#0412135",
              "liquibase.structure.core.Column#0412136",
              "liquibase.structure.core.Column#0412137"]
            ,
            "indexes": [
              "liquibase.structure.core.Index#0412134"]
            ,
            "name": "TABLE2",
            "outgoingForeignKeys": [
              "liquibase.structure.core.ForeignKey#0412138"]
            ,
            "primaryKey": "liquibase.structure.core.PrimaryKey#0412139",
            "schema": "liquibase.structure.core.Schema#0412101",
            "snapshotId": "0412133"
          }
        }]
      ,
      "liquibase.structure.core.UniqueConstraint": [
        {
          "uniqueConstraint": {
            "backingIndex": "liquibase.structure.core.Index#0412120",
            "clustered": false,
            "columns": [
              "liquibase.structure.core.Column#0412119"]
            ,
            "deferrable": false,
            "disabled": false,
            "initiallyDeferred": false,
            "name": "UC_TABLE1_COLUMN2",
            "snapshotId": "0412118",
            "table": "liquibase.structure.core.Table#0412117",
            "validate": false
          }
        }]
      
    },
    "snapshotControl": {
      "snapshotControl": {
        "includedType": [
          "com.datical.liquibase.ext.appdba.synonym.Synonym",
          "com.datical.liquibase.ext.storedlogic.checkconstraint.CheckConstraint",
          "com.datical.liquibase.ext.storedlogic.databasepackage.DatabasePackage",
          "com.datical.liquibase.ext.storedlogic.databasepackage.DatabasePackageBody",
          "com.datical.liquibase.ext.storedlogic.function.Function",
          "com.datical.liquibase.ext.storedlogic.trigger.Trigger",
          "liquibase.structure.core.Catalog",
          "liquibase.structure.core.Column",
          "liquibase.structure.core.ForeignKey",
          "liquibase.structure.core.Index",
          "liquibase.structure.core.PrimaryKey",
          "liquibase.structure.core.Schema",
          "liquibase.structure.core.Sequence",
          "liquibase.structure.core.StoredProcedure",
          "liquibase.structure.core.Table",
          "liquibase.structure.core.UniqueConstraint",
          "liquibase.structure.core.View"]
        
      }
    }
  }
}

support filter by childnode value

Seems this is not currently supported, calling the method below returns an error:
NoMethodError: undefined method `price' for "reference":String

JsonPath.on("{ "store": {"book": [{ "category": "reference", "price": 8.95} ] }}", "$..book[?(@.price==8.95)].category")

elements that contain @ or $ are unparsable

Unfortunately the data I'm receiving contains "@" symbols and the StringScanner utilization doesn't have a way of escaping special characters.

data = 
{"colors"=>
  [{"@r"=>255, "@g"=>0, "@b"=>0},
   {"@r"=>0, "@g"=>255, "@b"=>0},
   {"@r"=>0, "@g"=>0, "@b"=>255}]}

JsonPath.on(data, "$..\\@r")
# => []
# should be => [255, 0, 0] 

Regex filtering not working

I would like to delete element(s) of a JSON object matching a certain criteria, but a simple regex match doesn't seem to work.

json = <<-HERE_DOC
{"store":
  {"bicycle":
    {"price":19.95, "color":"red"},
    "book":[
      {"price":8.95, "category":"reference", "title":"Sayings of the Century", "author":"Nigel Rees"},
      {"price":12.99, "category":"fiction", "title":"Sword of Honour", "author":"Evelyn Waugh"},
      {"price":8.99, "category":"fiction", "isbn":"0-553-21311-3", "title":"Moby Dick", "author":"Herman Melville","color":"blue"},
      {"price":22.99, "category":"fiction", "isbn":"0-395-19395-8", "title":"The Lord of the Rings", "author":"Tolkien"}
    ]
  }
}
HERE_DOC

> JsonPath.new('$..book[?(@.author == "Herman Melville")]').on(json)
=> [{"price"=>8.99, "category"=>"fiction", "isbn"=>"0-553-21311-3", "title"=>"Moby Dick", "author"=>"Herman Melville", "color"=>"blue"}]

> JsonPath.new('$..book[?(@.author =~ /herman/i)]').on(json)
=> []

delete not working on array ?

the delete function does not seem to be working when the path contain array element. I tried on the example json in readme file

>> path
=> "$..store.book[0]"
>> pp JsonPath.for(json).delete(path)
#<JsonPath::Proxy:0x007f9110827f50
 @obj=
  {"store"=>
    {"bicycle"=>{"price"=>19.95, "color"=>"red"},
     "book"=>
      [{"price"=>8.95,
        "category"=>"reference",
        "title"=>"Sayings of the Century",
        "author"=>"Nigel Rees"},
       {"price"=>12.99,
        "category"=>"fiction",
        "title"=>"Sword of Honour",
        "author"=>"Evelyn Waugh"},
       {"price"=>8.99,
        "category"=>"fiction",
        "isbn"=>"0-553-21311-3",
        "title"=>"Moby Dick",
        "author"=>"Herman Melville",
        "color"=>"blue"},
       {"price"=>22.99,
        "category"=>"fiction",
        "isbn"=>"0-395-19395-8",
        "title"=>"The Lord of the Rings",
        "author"=>"Tolkien"}]}}>```

Float in Conditional Expression

I'm just getting around to trying out the conditional operators (thanks!) and found a potential bug. If i use the JSON example from the README that includes float values for book prices I get an error. More specifically, if I see an error when I try to use floats in the JsonPath expression.

json = <<-HERE_DOC
{"store":
  {"bicycle":
    {"price":19.95, "color":"red"},
    "book":[
      {"price":8.95, "category":"reference", "title":"Sayings of the Century", "author":"Nigel Rees"},
      {"price":12.99, "category":"fiction", "title":"Sword of Honour", "author":"Evelyn Waugh"},
      {"price":8.99, "category":"fiction", "isbn":"0-553-21311-3", "title":"Moby Dick", "author":"Herman Melville","color":"blue"},
      {"price":22.99, "category":"fiction", "isbn":"0-395-19395-8", "title":"The Lord of the Rings", "author":"Tolkien"}
    ]
  }
}
HERE_DOC

> JsonPath.new("$..book[?(@['price'] == 13 || @['price'] == 23)]").on(json)
=> []

> JsonPath.new("$..book[?(@['price'] == 8.95 || @['price'] == 22.99)]").on(json)
SyntaxError: (eval):1: `@[' is not allowed as an instance variable name
(eval):1: syntax error, unexpected end-of-input
(@['price'] == '8['']['95']' ||
  ^

This doesn't appear to have anything to do with conditional operators. If I take the simple example from the README...

> JsonPath.new('$..price[?(@ < 10)]').on(json)
=> [8.95, 8.99]

and try to use a float...

> JsonPath.new('$..price[?(@ < 10.0)]').on(json))
SyntaxError: (eval):1: `@' without identifiers is not allowed as an instance variable name
(eval):1: syntax error, unexpected end-of-input
(@ < 10['']['0'])

/cc @Skarlso

Newlines make JsonPath hang

I currently allow users to enter JsonPaths via a textarea which I then split on /\R/, but before I implemented the split functionality I noticed my background jobs were hanging/frozen.

How to recreate

json = { initial: true }.to_json

success_path = "$.initial"
JsonPath.on(json, success_path)
# => [true]

broken_path = "$.initial\n"
JsonPath.on(json, broken_path)
# just hangs, doesn't cause an exception or anythingโ€ฆ

@Skarlso

Unable to parse `dict.*.key` type query

Here's a sample script:

require 'jsonpath'

json = <<-HEREDOC
{
  "nodes": {
    "foo": {
      "duh": 1
    },
    "bar": {
      "duh": 2
    },
    "quux": {
      "duh": 3
    }
  }
}
HEREDOC

path = JsonPath.new('$.nodes.*.duh')
puts path.on(json)

Running the script produces empty result, while I'm expecting [1, 2, 3]. Testing the json on http://www.jsonquerytool.com/#/JSONPath gives me the right result:

image

Discrepancy with reference implementation - same result for $.store and $.store.*

I noticed a discrepancy between this project and the original PHP implementation of JSONPath by Stefan Gรถssner. This project gives the same result for $.store and $.store.*, while the original PHP implementation returns different results.

Additionally, I can't figure out a workaround. Is there a jsonpath that would give me an equivalent result (values only instead of the full object)?

I tested this with the sample data from the original original jsonpath article:

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

I saved that to store.json and tested with:

jsonpath '$.store' store.json
jsonpath '$.store.*' store.json

and compared with the results at http://jsonpath.curiousconcept.com/

subscript on filtered set

Hi, I was expecting to be able to subscript a filtered set, but get back an empty set. e.g.

$.store..books[?(@['category']=='fiction')].[0]

Maybe this is unsupported, maybe it's a bug, I don't know.

Below is a shell script where the first two paths return expected results, PATHEXP3 returns an empty set.

Any hints appreciated, thanks

PATHEXP1="$.store..books[?(@['category']=='fiction')]"                               
PATHEXP2="$.store..books[0]"                                                         
PATHEXP3="$.store..books[?(@['category']=='fiction')].[0]"                           
                                                                                     
jsonpath $PATHEXP3 <<EOF                                                             
{ "store": [                                                                         
    { "books": [                                                                     
      { "category": "reference",                                                     
        "author": "Nigel Rees",                                                      
        "title": "Sayings of the Century",                                           
        "price": 8.95                                                                
      },                                                                             
      { "category": "fiction",                                                       
        "author": "Evelyn Waugh",                                                    
        "title": "Sword of Honour",                                                  
        "price": 12.99                                                               
      },                                                                             
      { "category": "fiction",                                                       
        "author": "Herman Melville",                                                 
        "title": "Moby Dick",                                                        
        "isbn": "0-553-21311-3",                                                     
        "price": 8.99                                                                
      },                                                                             
      { "category": "fiction",                                                       
        "author": "J. R. R. Tolkien",                                                
        "title": "The Lord of the Rings",                                            
        "isbn": "0-395-19395-8",                                                     
        "price": 22.99                                                               
      } ] },                                                                         
    { "bicycles": [                                                                  
      {                                                                              
        "color": "red",                                                              
        "price": 19.95                                                               
      }                                                                              
    ] }                                                                              
] }                                                                                  
EOF 

Regression in 0.7.0

require 'json'
require 'jsonpath'

json = {
  ok: true,
  channels: [
    {
      id: 'C09C5GYHF',
      name: 'general'
    },
    {
      id: 'C09C598QL',
      name: 'random'
    }
  ]
}.to_json

p JsonPath.on(json, "$..[?(@.name=='general')].id")

With 0.5.8

["C09C5GYHF"]

With 0.7.0 or newer

["C09C5GYHF", "C09C5GYHF", "general"]

I am possibly misunderstanding the syntax?

Parse non string as string

Hello, im trying to parse this type of json:
{
"user":[
{
"id": "123",
"name": "example"
}
]
}
Using this expression '$.user[?(@["id"] == "123")]' does not work because the "id" field is a number. If a put "123a", like a real string, works perfectly. Is there a way to solve this? Thanks

Curation of this gem and maintenance of this Gem

Hi.

So, I'm in desperate need of an update for this Gem and it hasn't been curated / updated in a LONG while, even though there are some much needed pending PRs in it.

I'd be happy to take over the maintenance of it if you would like me to @joshbuddy.

Cheers,
Skarlso.

Are any string functions implemented?

Seems from the code there's no way to do something equivalent to this in XPath:

(Pulled from the example on http://en.wikipedia.org/wiki/XPath)

/wikimedia/projects/project[@name="Wikipedia"]/editions/edition/text()

Is it not possible to filter based on string values yet? Are there any plans to implement this, along with the other string functions?

Support returning null for missing leaves in a path

I'm not sure if this is supported already since I see that process_function_or_literal accepts a default value. But basically, if we have JSON that looks like this

{
  "data": [
     {
        "name" : "john",
        "gender" : "male"
    },
    {
        "name" : "ben"
    }
  ]
}

then the path data[*].gender will return (as expected)

["male"]

However, there are situations where a nil value is preferred to be returned instead if the leaf/key is missing. So perhaps something like

JsonPath.new('data[*].gender', default: nil).on(object)

will instead return

["male", nil]

This is already supported actually in the Java library: https://github.com/json-path/JsonPath

I don't mind submitting a pull request that supports this unless you know of a quick solution, thanks!

@Skarlso

gsub fails for where selectors

Try the below in the specs:
JsonPath.for(@object).gsub!("$..book[?(@['price'] > 20)]") { |p| p + 10 }

It fails with trace:
NoMethodError: undefined method +' for nil:NilClass from (irb):139:inblock in irb_binding'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:97:in call' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:97:inyield_value'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:15:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:38:inblock (2 levels) in each'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/activesupport-4.1.6/lib/active_support/core_ext/range/each.rb:7:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/activesupport-4.1.6/lib/active_support/core_ext/range/each.rb:7:ineach_with_time_with_zone'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:35:in block in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:24:ineach'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:24:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:29:inblock in each'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:24:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:24:ineach'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:78:in block in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:78:ineach'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:78:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:18:ineach'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:20:in each' from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/proxy.rb:40:in_gsub'
from /Users/simon/.rvm/gems/ruby-2.1.2@lookist/gems/jsonpath-0.5.6/lib/jsonpath/proxy.rb:15:in gsub!' from (irb):139 from /Users/simon/.rvm/rubies/ruby-2.1.2/bin/irb:11:in

'

Cannot `gsub` with a PATH_ALL expression.

First, thanks for making jsonpath! It's great!

Second, can you help with this error? Should it not be possible to do the following?

JsonPath.for('{"a": "b"}').gsub('$..*'){|v| v }.to_hash

This fails with:

TypeError: no implicit conversion of nil into Hash
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:103:in `replace'
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:103:in `yield_value'
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:15:in `each'
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:18:in `each'
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:18:in `each'
	from /Users/david/projects/soomo/vendor/bundle/ruby/2.3.0/gems/jsonpath-0.8.10/lib/jsonpath/enumerable.rb:20:in `each'
	...

I get the same failure with:

JsonPath.for('{}').gsub('$..*'){|v| v }

I am able to do:

JsonPath.for('{"a": "b"}').gsub('$..a'){|v| v.is_a?(String) ? v.upcase : v }.to_hash

with the expected results. My particular use case is to operate on all the strings contained within the document in the block given to #gsub.

Support for OR (||) operator?

I've tried a few things based on suggestions in this post but am having no luck. Obviously there are many different implementations of JSONPath so I'm wondering if this one has support for an OR operator.

As an illustration, the following example works with the online evaluator:

// JSON
{
  "firstName": "John",
  "phoneNumbers": [
    {
      "type"  : "iPhone",
      "number": "0123-4567-8888"
    },
    {
      "type"  : "home",
      "number": "0123-4567-8910"
    }
  ]
}
// query
$.phoneNumbers[?(@.type == 'home' || @.type == 'iPhone')]
// desired output
[
  {
    "type": "iPhone",
    "number": "0123-4567-8888"
  },
  {
    "type": "home",
    "number": "0123-4567-8910"
  }
]

condition for children does not work

It was fixed for array here: #24

But still does not work, if a child is not an array:

json = <<-JSON
{
  "store": {
    "book": {
      "category": "reference",
      "author": "Nigel Rees",
      "title": "Sayings of the Century",
      "price": 8.95
    }
  }
}
JSON

pp JsonPath.new("$..book[?(@.price<10)]").on(json) # => []
# But expected to return the book

# But checking price directly works also
pp JsonPath.new("$..book.price[?(@<10)]").on(json) # => [8.95]

Can't parse a specific pattern

@Skarlso not sure how to explain this issue, so just going to provide example JSON, Path, expected output and actual output. Let me know if you need further information.

This works on jsonpath.com (but returns [] with JsonPath.on.

JSON

{
  "ports": {
    "extraports": {
      "count": "996",
      "state": "filtered",
      "extrareasons": {
        "count": "996",
        "reason": "no-responses"
      }
    },
    "port": [
      {
        "portid": "80",
        "protocol": "tcp",
        "service": {
          "conf": "3",
          "method": "table",
          "name": "http"
        },
        "state": {
          "reason": "syn-ack",
          "reason_ttl": "44",
          "state": "open"
        }
      },
      {
        "portid": "443",
        "protocol": "tcp",
        "service": {
          "conf": "3",
          "method": "table",
          "name": "https"
        },
        "state": {
          "reason": "syn-ack",
          "reason_ttl": "44",
          "state": "open"
        }
      },
      {
        "portid": "2222",
        "protocol": "tcp",
        "service": {
          "conf": "3",
          "method": "table",
          "name": "EtherNetIP-1"
        },
        "state": {
          "reason": "syn-ack",
          "reason_ttl": "45",
          "state": "open"
        }
      },
      {
        "portid": "3128",
        "protocol": "tcp",
        "service": {
          "conf": "3",
          "method": "table",
          "name": "squid-http"
        },
        "state": {
          "reason": "syn-ack",
          "reason_ttl": "44",
          "state": "open"
        }
      }
    ]
  }
}

JsonPath

Version 1

$..[?(/http/.test(@.name))]

Expected output

[
  {
    "conf": "3",
    "method": "table",
    "name": "http"
  },
  {
    "conf": "3",
    "method": "table",
    "name": "https"
  },
  {
    "conf": "3",
    "method": "table",
    "name": "squid-http"
  }
]

Version 2

$.ports.port[?(/http/.test(@.service.name))]

Expected output

[
  {
    "portid": "80",
    "protocol": "tcp",
    "service": {
      "conf": "3",
      "method": "table",
      "name": "http"
    },
    "state": {
      "reason": "syn-ack",
      "reason_ttl": "44",
      "state": "open"
    }
  },
  {
    "portid": "443",
    "protocol": "tcp",
    "service": {
      "conf": "3",
      "method": "table",
      "name": "https"
    },
    "state": {
      "reason": "syn-ack",
      "reason_ttl": "44",
      "state": "open"
    }
  },
  {
    "portid": "3128",
    "protocol": "tcp",
    "service": {
      "conf": "3",
      "method": "table",
      "name": "squid-http"
    },
    "state": {
      "reason": "syn-ack",
      "reason_ttl": "44",
      "state": "open"
    }
  }
]

Both return

[]

Delete doesnt delte more than 7 items from an array

a = {:itemList=>
  [{:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"},
   {:alfa=>"beta"}]}

JsonPath.for(a.to_json).delete('$..itemList[1:10]')

returns

=> #<JsonPath::Proxy:0x000000014a0208
 @obj=
  {"itemList"=>
    [{"alfa"=>"beta"}, {"alfa"=>"beta"}, {"alfa"=>"beta"}, {"alfa"=>"beta"}, {"alfa"=>"beta"}, {"alfa"=>"beta"}]}>

Which is too much of items returned. I can switch the 1:10 for 1:7 or 1:8, doesn't make a difference

Selecting multiple keys/values

Hi @Skarlso not sure if this is a bug per se, but I can't seem to get this to work (even on jsonpath.com) but can on other evaluators.

JSON

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99
            }
        ]
}

JSONPath

$.store.book[*]['category', 'author']

Desired outcome

[
   {
      "category" : "reference",
      "author" : "Nigel Rees"
   },
   {
      "category" : "fiction",
      "author" : "Evelyn Waugh"
   }
]

using gsub! path, false gives an error

I cant replace a path with false. replacing with true works nicely.

doc.gsub! path, false
NoMethodError: undefined method `call' for nil:NilClass
from /usr/local/var/rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/jsonpath-0.5.6/lib/jsonpath/enumerable.rb:95:in `yield_value'

Selecting data based on fields with boolean value doesnt work

given the following json

{
  "firstName": "John",
  "lastName" : "doe",
  "age"      : 12,
  "address"  : {
    "streetAddress": "naist street",
    "city"         : "Nara",
    "postalCode"   : "630-0192"
  },
  "phoneNumbers": [
    {
      "type"  : "iPhone",
      "number": "0123-4567-8888"
    },
    {
      "type"  : true,
      "number": "0123-4567-8910"
    }
  ]
}

Im trying to extract phoneNumbers where the type is true

this is my expression : $.phoneNumbers[?(@.type==true)] but it doesn't work

I have a lot of data sets with this boolean fields and I cant use JsonPath to work them out .

Any suggestion ?

Parentheses are not supported in a matcher

Failing test case:

  def test_parens_in_value
    data = {
      'data' => {
        'number' => '(492) 080-3961'
      }
    }
    assert_equal [{'number'=>'(492) 080-3961'}], JsonPath.new("$.data[?(@.number == '(492) 080-3961')]").on(data)
  end

Output:

1) Failure:
TestJsonpath#test_parens_in_value [test/test_jsonpath.rb:354]:
Expected: [{"number"=>"(492) 080-3961"}]
  Actual: []

The failure appears to come from:

exp = exp.sub(/@/, '').gsub(/[\(\)]/, '').gsub(/"/, '\'').strip

jsonpath eval not working

I am trying to get name of of all states from json where 'hasAgency' is true

I tried JsonPath.new("$..name[?(@['hasAgency'] == true)]").on(json_response) and it returns empty array

I also tried JsonPath.new("$..name[?(@['hasAgency'] != false)]").on(json_response) and it returns error "undefined method `strip' for nil:NilClass"

I tried jsonpath $[?(@['hasAgency'] != false)].name at http://jsonpath.com/ and it work so I vene tried that path but it is yielding the same result as well.

Here's the JSON:

[ { "name": "Alabama", "abbrev": "AL", "hasAgency": true }, { "name": "Alaska", "abbrev": "AK", "hasAgency": false }, { "name": "Arizona", "abbrev": "AZ", "hasAgency": true }, { "name": "Arkansas", "abbrev": "AR", "hasAgency": true }, { "name": "California", "abbrev": "CA", "hasAgency": true }, { "name": "Colorado", "abbrev": "CO", "hasAgency": true }, { "name": "Connecticut", "abbrev": "CT", "hasAgency": true }, { "name": "Delaware", "abbrev": "DE", "hasAgency": true }, { "name": "Florida", "abbrev": "FL", "hasAgency": true }, { "name": "Georgia", "abbrev": "GA", "hasAgency": true }, { "name": "Hawaii", "abbrev": "HI", "hasAgency": true }, { "name": "Idaho", "abbrev": "ID", "hasAgency": true }, { "name": "Illinois", "abbrev": "IL", "hasAgency": true }, { "name": "Indiana", "abbrev": "IN", "hasAgency": true }, { "name": "Iowa", "abbrev": "IA", "hasAgency": true }, { "name": "Kansas", "abbrev": "KS", "hasAgency": true }, { "name": "Kentucky", "abbrev": "KY", "hasAgency": true }, { "name": "Louisiana", "abbrev": "LA", "hasAgency": true }, { "name": "Maine", "abbrev": "ME", "hasAgency": true }, { "name": "Maryland", "abbrev": "MD", "hasAgency": true }, { "name": "Massachusetts", "abbrev": "MA", "hasAgency": true }, { "name": "Michigan", "abbrev": "MI", "hasAgency": true }, { "name": "Minnesota", "abbrev": "MN", "hasAgency": true }, { "name": "Mississippi", "abbrev": "MS", "hasAgency": true }, { "name": "Missouri", "abbrev": "MO", "hasAgency": true }, { "name": "Montana", "abbrev": "MT", "hasAgency": true }, { "name": "Nebraska", "abbrev": "NE", "hasAgency": true }, { "name": "Nevada", "abbrev": "NV", "hasAgency": true }, { "name": "New Hampshire", "abbrev": "NH", "hasAgency": true }, { "name": "New Jersey", "abbrev": "NJ", "hasAgency": true }, { "name": "New Mexico", "abbrev": "NM", "hasAgency": true }, { "name": "New York", "abbrev": "NY", "hasAgency": true }, { "name": "North Carolina", "abbrev": "NC", "hasAgency": true }, { "name": "North Dakota", "abbrev": "ND", "hasAgency": true }, { "name": "Ohio", "abbrev": "OH", "hasAgency": true }, { "name": "Oklahoma", "abbrev": "OK", "hasAgency": true }, { "name": "Oregon", "abbrev": "OR", "hasAgency": true }, { "name": "Pennsylvania", "abbrev": "PA", "hasAgency": true }, { "name": "Puerto Rico", "abbrev": "PR", "hasAgency": true }, { "name": "Rhode Island", "abbrev": "RI", "hasAgency": true }, { "name": "South Carolina", "abbrev": "SC", "hasAgency": true }, { "name": "South Dakota", "abbrev": "SD", "hasAgency": true }, { "name": "Tennessee", "abbrev": "TN", "hasAgency": true }, { "name": "Texas", "abbrev": "TX", "hasAgency": true }, { "name": "Utah", "abbrev": "UT", "hasAgency": true }, { "name": "Vermont", "abbrev": "VT", "hasAgency": true }, { "name": "Virginia", "abbrev": "VA", "hasAgency": true }, { "name": "Washington", "abbrev": "WA", "hasAgency": true }, { "name": "West Virginia", "abbrev": "WV", "hasAgency": true }, { "name": "Wisconsin", "abbrev": "WI", "hasAgency": true }, { "name": "Wyoming", "abbrev": "WY", "hasAgency": false } ]

It's alive!

Don't let the lack of updates discourage you.

There aren't any because it's pretty much feature complete. I might still hack around on it to improve it in some ways, but there aren't too many things I can do at this point.

I'm promptly answering issues though, if any should arise.

Cheers,
Gergely.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.