Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jsonpath: evaluation throws on json containing json_const_pointer #554

Closed
mosswaldcognex opened this issue Nov 18, 2024 · 3 comments
Closed

Comments

@mosswaldcognex
Copy link

Calling jsonpath_expression::evaluate() on a JSON that contains a json_const_pointer to a valid second JSON throws "Index on non-array value not supported". The reason seems to be a non-const call to basic_json::at[int i] when selecting an array object.

This behavior exist in the latest release 0.178.0. I used release 0.170.2 before which did not show this exception. There everything worked fine.
The attached example_code.txt shows how to reproduce the error.

  • Compiler: gcc11.4, VS2022
  • Architecture (e.g. x86, x64) x86/x64, arm64
  • Operating system: windows, linux
@danielaparker
Copy link
Owner

danielaparker commented Nov 19, 2024

Yes, in 0.172.0 there were some changes to jsonpath::make_expression, basically to support creating expressions that would allow update operations on the root JSON value, in addition to select. My goal was to make these changes entirely transparent to users, but it seems not to be the case for root json values containing json_const_pointer elements.

You can still run this query by changing the template parameter in make_expression from jsoncons::json to const jsoncons::json, viz,

#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/jsonpath.hpp>
#include <iostream>

int main()
{
    jsoncons::json root_works = jsoncons::json::parse(R"(
{
    "books":
    [
        {
            "category": "fiction",
            "title" : "The Night Watch",
            "author" : "Sergei Lukyanenko",
            "price" : 23.58
        },
        {
            "category": "fiction",
            "title" : "The Comedians",
            "author" : "Graham Greene",
            "price" : 21.99
        }
    ]
}
)");

    jsoncons::json nested_json = jsoncons::json::parse(R"(
[
    {
        "category": "fiction",
        "title" : "The Night Watch",
        "author" : "Sergei Lukyanenko",
        "price" : 23.58
    },
    {
        "category": "fiction",
        "title" : "The Comedians",
        "author" : "Graham Greene",
        "price" : 21.99
    }
]
)");

    jsoncons::json now_works = root_works;
    now_works["books"] = jsoncons::json(jsoncons::json_const_pointer_arg, &nested_json);

    auto expr = jsoncons::jsonpath::make_expression<const jsoncons::json>("$.books[?(@.price > avg($.books[*].price))].title");

    try
    {
        jsoncons::json result_works = expr.evaluate(root_works);
        jsoncons::json result_now_works = expr.evaluate(now_works);

        std::cout << result_now_works << "\n";
    }
    catch (const std::exception& e)
    {
        std::cout << e.what() << "\n";
    }
}

Output:

["The Night Watch"]

There's a bit of an issue with json_const_pointer_arg because the root json value becomes half non-const, and half const. The compiler doesn't prevent you from calling functions like insert_or_assign on the root, but the contained json_const_pointer elements will throw on any except const function calls. It puts some burden on the user.

The next version (0.179.0) will support a json value containing non-const references to other json values, and you'll be able to write it as follows:

    jsoncons::json now_works = root_works;
    now_works["books"] = jsoncons::json(jsoncons::json_reference_arg, nested_json);

    auto expr = jsoncons::jsonpath::make_expression<jsoncons::json>("$.books[?(@.price > avg($.books[*].price))].title");

    try
    {
        jsoncons::json result_works = expr.evaluate(root_works);
        jsoncons::json result_now_works = expr.evaluate(now_works);

        std::cout << result_now_works << "\n";
    }
    catch (const std::exception& e)
    {
        std::cout << e.what() << "\n";
    }

json_reference_arg is currently supported on master. Unlike with json_const_pointer_arg, all json functions and operators, both accessors and mutators, can be called on a json value containing json_reference_arg elements. Mutators will mutate the referenced elements. And if you don't want mutation, you can always take a const reference to the value and work with that.

@mosswaldcognex
Copy link
Author

Many thanks for the detailed explanation! Sorry that I did not immediately recognize the change in 0.172.0. The problem is therefore solved for me. I will test the new value type json_reference_arg at the next opportunity.

@danielaparker
Copy link
Owner

Starting with the next release, code currently on master, this won't happen anymore, even with make_expression<json>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants