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

add json path functionality to extract values #821

Open
cdaller opened this issue Sep 28, 2018 · 5 comments
Open

add json path functionality to extract values #821

cdaller opened this issue Sep 28, 2018 · 5 comments

Comments

@cdaller
Copy link

cdaller commented Sep 28, 2018

I have an application that fetches a json file from an http server and extracts a value from the json. The url (and therefore the structure of the json) is configurable during runtime. So I wanted to use a json path to define which value to extract from the json content. I googled, but could not find any, so I wrote a very simple jsonPath parser.

I think a method like this would be quite useful in the library - what do you think?

As I am not very experienced in C++, the method could (and probably should) be rewritten before being integrated :-)

// parse jsonPaths like $.foo[1].bar.baz[2][3].value (equals to foo[1].bar.baz[2][3].value)
float parseJson(char* jsonString, char *jsonPath) {
    float jsonValue;
    DynamicJsonBuffer jsonBuffer;
    
    JsonVariant root = jsonBuffer.parse(jsonString);
    JsonVariant element = root;

    if (root.success()) {
        // parse jsonPath and navigate through json object:
        char pathElement[40];
        int pathIndex = 0;

        printf("parsing '%s'\n", jsonPath);
        for (int i = 0; jsonPath[i] != '\0'; i++){
            if (jsonPath[i] == '$') {
                element = root;
            } else if (jsonPath[i] == '.') {
                if (pathIndex > 0) {
                    pathElement[pathIndex++] = '\0';
                    // printf("pathElement '%s'\n", pathElement);
                    pathIndex = 0;
                    element = element[pathElement];
                    if (!element.success()) {
                        printf("failed to parse key %s\n", pathElement);
                    }
                }
            } else if ((jsonPath[i] >= 'a' && jsonPath[i] <= 'z') 
                    || (jsonPath[i] >= 'A' && jsonPath[i] <= 'Z') 
                    || (jsonPath[i] >= '0' && jsonPath[i] <= '9')
                    || jsonPath[i] == '-' || jsonPath[i] == '_'
                    ) {
                pathElement[pathIndex++] = jsonPath[i];
            } else if (jsonPath[i] == '[') {
                if (pathIndex > 0) {
                    pathElement[pathIndex++] = '\0';
                    // printf("pathElement '%s'\n", pathElement);
                    pathIndex = 0;
                    element = element[pathElement];
                    if (!element.success()) {
                        printf("failed in parsing key %s\n", pathElement);
                    }
                }
            } else if (jsonPath[i] == ']') {
                pathElement[pathIndex++] = '\0';
                int arrayIndex = strtod(pathElement, NULL);
                // printf("index '%s' = %d\n", pathElement, arrayIndex);
                pathIndex = 0;
                element = element[arrayIndex];
                if (!element.success()) {
                    printf("failed in parsing index %d\n", arrayIndex);
                }
            }
        }  
        // final token if any:
        if (pathIndex > 0) {
            pathElement[pathIndex++] = '\0';
            // printf("pathElement '%s'\n", pathElement);
            pathIndex = 0;
            element = element[pathElement];
            if (!element.success()) {
                printf("failed in parsing key %s\n", pathElement);
            }
        }

        jsonValue = element.as<float>();

        //jsonValue = measurements[1]["sensordatavalues"][0]["value"];
        printf("success reading value: %f\n", jsonValue);
    } else {
        jsonValue = -1; // should be NaN or something similar
        printf("could not parse json for value");
    }
    return jsonValue;
}
@bblanchon
Copy link
Owner

bblanchon commented Sep 29, 2018

Hi @cdaller,

I like this idea; I think it would be a great feature for the library.
I'm adding this feature to the to-do list.
However, I'm quite busy with other features of v6 right now, so please be patient.

BTW, I think a signature like the following would offer greater flexibility:

JsonVariant resolve(JsonVariant, const char* path);

Regards,
Benoit

@cdaller
Copy link
Author

cdaller commented Sep 30, 2018

Another optimization: using a JsonPath object that does the parsing of the path only once, so mulitple extraction of the data of the same path will be more efficient. I think, this is a common behaviour - using the same path again and again.

JsonPath parser should extract the key names and array indices, so resolving it to a value will become more efficient.

Future parsers might support filter expressions or other features from http://goessner.net/articles/JsonPath/ - maybe we should have a look at https://github.com/danielaparker/jsoncons/blob/master/doc/ref/jsonpath/jsonpath.md

// simple usage (not so efficient)
JsonVariant resolve(JsonVariant, const char* path);

// more efficient usage (optimize parsing)
JsonVariant resolve(JsonVariant, JsonPath);

@omar-bb
Copy link

omar-bb commented Sep 21, 2020

Hey @bblanchon

has this feature been added since or not?

@bblanchon
Copy link
Owner

Not exactly a JSON Path feature, but the function presented in #1505 (comment) can be used as a compile-time alternative.

@bblanchon
Copy link
Owner

Please see #1904 (comment) for another basic implementation that support member access via "parent.child".
It's not following the JSON Path syntax, but it should be good enough for many projects.

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

3 participants