ArduinoJson
ArduinoJson copied to clipboard
JSONPath
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;
}
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
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);
Hey @bblanchon
has this feature been added since or not?
Not exactly a JSON Path feature, but the function presented in https://github.com/bblanchon/ArduinoJson/issues/1505#issuecomment-782825946 can be used as a compile-time alternative.
Please see https://github.com/bblanchon/ArduinoJson/issues/1904#issuecomment-1498614172 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.