Galbar / JsonPath-PHP

A JsonPath implementation in PHP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Return non-existent values from getter function

ShaneOH opened this issue · comments

Hey, I'm looking to have a result set with an even amount of values. Take the following example:

$jsonPath = new JsonObject(
'{
  "products": [
    {
      "sku": "ABC",
      "stock": 1,
      "description": "A short description"
    },
    {
      "sku": "DEF",
      "stock": 2
    },
    {
      "sku": "GHI",
      "stock": 3,
      "description": "Another short description"
    }
  ]
}');

$fields = [
    'sku' => '$.products.*.sku',
    'stock' => '$.products.*.stock',
    'description' => '$.products.*.description'
];

$data = [];

foreach ($fields as $field => $query) {
    $data[$field] = $jsonPath->get($query);
}

print_r($data);

This results in the following:

Array
(
    [sku] => Array
        (
            [0] => ABC
            [1] => DEF
            [2] => GHI
        )

    [stock] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

    [description] => Array
        (
            [0] => A short description
            [1] => Another short description
        )

)

However, I'm looking to have an empty placeholder in the correct order if the key doesn't exist in the JSON. If I go into the library and change $createInexistent in getReal from false (its default value) to true, then this results in what I'm actually looking for:

Array
(
    [sku] => Array
        (
            [0] => ABC
            [1] => DEF
            [2] => GHI
        )

    [stock] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
        )

    [description] => Array
        (
            [0] => A short description
            [1] => Array
                (
                )

            [2] => Another short description
        )

)

The issue is that this parameter is always set privately.

Would a Pull Request to add a second parameter to the public get function: $createInexistent, (which defaults to false and is thus backwards compatible) that passes through to getReal be accepted? Or is there an alternate solution to what I'm looking to do?

Hi! Thank you for raising this.

I've given it a long thought. Even though it would be nice for this case, this behavior would break things in many other aspects of JSON Path querying.

JSON Path matches the path you give it to get the result. That way you can do $..foobar and it will return the values of any foobar field at any point in the hierarchy of the data you are querying.

If your suggestion was implemented, making the behavior consistent for any query that diverges (that is any query that has a .. or a * or something like ['foo', 'bar', 1, 2]), the result would be unusable in many cases. Imagine it with the above example: $..foobar. For every field in the whole object, at every level, you'd get a result, be it the actual value or a fallback value:

$jsonPath = new JsonObject('{
  "a": {
    "foo": 1,
    "foobar": 3
  },
  "b": false
}');

print_r($jsonPath->{'$..foobar'});

Array(
  NULL,  // $.foobar
  3,     // $.a.foobar
  NULL,  // $.a.foo.foobar
  NULL,  // $.a.foobar.foobar
  NULL   // $.b.foobar
);

My suggestion for this case is: normalize your data before querying. Make it so that all elements in the array follow the same structure, at least for the fields you are querying.

Hey @Galbar thanks for responding. Makes sense, and I understand the limitations this change would introduce now that you bring them up. I did end up working around my own issue with some normalizing -- namely querying a "root" path to get a series of sub-objects (products in my own example), and then making separate queries for each field in a loop against each sub-object. More costly but still fast enough, gets the job done and keeps the result set stable.

Thanks for your input! I'll go ahead and close this issue now.