Рекурсивный ввод объекта Json на основе строки

У меня есть следующий объект json:

{
    "ESP_IN_PC_OUT":{
        "track":{
            "1":{
                "mute":{
                    "type":"LED",
                    "pins":[4]
                }
            },
            "2":{
                "mute":{
                    "type":"LED",
                    "pins":[5]
                }
            }
        }
    }
}

Я хочу ввести объект, в зависимости от строки формата "/index1/index2/index3..." (строка является адресом сообщения OSC). Например, «/track/2/mute» должен возвращать объект Json следующим образом:

{
    "type":"LED",
    "pins":[5]
}

Чтобы я мог получить доступ к типу, массиву контактов и некоторым другим значениям, которые будут реализованы позже.

Я использую библиотеку Arduino JSON на ESP8266. Это код, который я пробовал:

#include <ArduinoJson.h>

char *jsonstr = "{\n    \"ESP_IN_PC_OUT\":{\n        \"track\":{\n            \"1\":{\n                \"mute\":{\n                    \"type\":\"LED\",\n                    \"pins\":[4]\n                }\n            },\n            \"2\":{\n                \"mute\":{\n                    \"type\":\"LED\",\n                    \"pins\":[5]\n                }\n            }\n        }\n    },\n\n\n    \"ESP_OUT_PC_IN\":{\n        \"analog\":{\n        },\n        \"digital\":{\n            \"16\":\"/track/1/mute\"\n        }\n    }\n}"; 

void setup() {
  Serial.begin(115200);

  DynamicJsonBuffer jsonBuffer;

  JsonObject& root = jsonBuffer.parseObject(jsonstr);
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }

  Serial.println((const char*) root["ESP_IN_PC_OUT"]["track"]["2"]["mute"]["type"]); // prints "LED"
  Serial.println();

  char *address = "/track/2/mute";     // _____Interesting part_____
  char *tmp = strtok(address,"/");
  JsonObject& tmpJson = root["ESP_IN_PC_OUT"];
  bool success = true;
  while(tmp != NULL && success == true){
    Serial.println(tmp);
    if(tmpJson.containsKey(tmp)){
      tmpJson = tmpJson[tmp];        // Error: use of deleted function 'ArduinoJson::JsonObject& ArduinoJson::JsonObject::operator=(const ArduinoJson::JsonObject&)'
      tmp = strtok(NULL,"/");
    } else {
      success = false;
    }
  }
  if(success == true){
    Serial.println((const char*)tmpJson["type"]);
  } else {
    Serial.println("Address not found");
  }
}

void loop() {
}

Однако это дает мне следующее сообщение об ошибке:

JSON_OSC_settings2:26: error: use of deleted function 'ArduinoJson::JsonObject& ArduinoJson::JsonObject::operator=(const ArduinoJson::JsonObject&)'

       tmpJson = tmpJson[tmp];

               ^

In file included from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.hpp:12:0,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\JSON_OSC_settings2\JSON_OSC_settings2.ino:1:

C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson/JsonObject.hpp:38:7: note: 'ArduinoJson::JsonObject& ArduinoJson::JsonObject::operator=(const ArduinoJson::JsonObject&)' is implicitly deleted because the default definition would be ill-formed:

 class JsonObject : public Internals::JsonPrintable<JsonObject>,

       ^

In file included from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson/JsonArray.hpp:13:0,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.hpp:11,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\JSON_OSC_settings2\JSON_OSC_settings2.ino:1:

C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson/Internals/ReferenceType.hpp:32:18: error: 'ArduinoJson::Internals::ReferenceType& ArduinoJson::Internals::ReferenceType::operator=(const ArduinoJson::Internals::ReferenceType&)' is private

   ReferenceType& operator=(const ReferenceType&);

                  ^

In file included from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.hpp:12:0,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/ArduinoJson.h:8,

                 from C:\Users\Pieter\Documents\Arduino\JSON_OSC_settings2\JSON_OSC_settings2.ino:1:

C:\Users\Pieter\Documents\Arduino\libraries\ArduinoJson-master/include/ArduinoJson/JsonObject.hpp:38:7: error: within this context

 class JsonObject : public Internals::JsonPrintable<JsonObject>,

       ^

exit status 1
use of deleted function 'ArduinoJson::JsonObject& ArduinoJson::JsonObject::operator=(const ArduinoJson::JsonObject&)'

Как я могу это решить? Какой другой подход я мог бы выбрать?


person tttapa    schedule 29.08.2016    source источник
comment
почему вы пытаетесь переопределить ссылку?   -  person Sugar    schedule 29.08.2016


Ответы (3)


Я не разработчик C, но то, что может вам помочь, называется «JSONPath», что должно быть аналогом xPath для JSON. Вероятно, вы можете найти библиотеку, которая может это сделать.

Это похоже.

person philipp    schedule 29.08.2016
comment
Кажется, это действительно правильное решение, спасибо! Создатель библиотеки также ответил на мой вопрос на GitHub. . - person tttapa; 29.08.2016
comment
Если это решит вашу проблему, вы можете свободно принять ответ. Это будет мой первый ответ на вопрос С++, и, если он будет принят, какой-то настоящий удачный выстрел :) - person philipp; 30.08.2016
comment
@tttapa: если это решит вашу проблему, было бы неплохо поделиться ответом с github здесь. - person mpromonet; 30.08.2016

Я написал функцию для анализа простой строки пути json и извлечения значения из строки json. Подробнее см. https://github.com/bblanchon/ArduinoJson/issues/821. .

Недавно я адаптировал его к ArduinoJson6 — должен признать, что я не специалист по С++, так что пожалейте мой код:

// parse jsonPaths like $.foo[1].bar.baz[2][3].value equals to foo[1].bar.baz[2][3].value
String parseJson(char* jsonString, char *jsonPath) {

    Serial.printf("parsing String '%s'\n", jsonString);

    String jsonValue;
    DynamicJsonDocument jsonBuffer(64000); // fixme!!!!

    DeserializationError err = deserializeJson(jsonBuffer, jsonString);
    JsonVariant root = jsonBuffer.as<JsonVariant>();
    JsonVariant element = root;

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

        Serial.printf("parsing Path '%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 == nullptr) {
                        Serial.printf("failed to parse key %s\n", pathElement);
                        return nullptr;
                    }
                }
            } 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 == nullptr) {
                        Serial.printf("failed in parsing key %s\n", pathElement);
                        return nullptr;
                    }
                }
            } 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 == nullptr) {
                    Serial.printf("failed in parsing index %d\n", arrayIndex);
                    return nullptr;
                }
            }
        }  
        // final token if any:
        if (pathIndex > 0) {
            pathElement[pathIndex++] = '\0';
            // printf("pathElement '%s'\n", pathElement);
            pathIndex = 0;
            element = element[pathElement];
            if (element == nullptr) {
                Serial.printf("failed in parsing key %s\n", pathElement);
                return nullptr;
            }
        }

        jsonValue = element.as<String>();
    } else {
        jsonValue = nullptr;
    }
    return jsonValue;
}

person cdaller    schedule 15.01.2020

Чтобы указать на подобъект, вы можете просто использовать:

JsonObject& tmpJson = root["ESP_IN_PC_OUT"]["track"]["2"]["mute"];

Затем вы можете проверить, существует ли этот подобъект, сравнивая его с JsonObject::invalid следующим образом:

bool success = (tmpJson != JsonObject::invalid());

if(success == true){
    Serial.println((const char*)tmpJson["type"]);
} else {
    Serial.println("Address not found");
}

Если часть пути не существует, operator[] вернет ссылку на JsonObject::invalid, тогда доступ к его дочернему элементу также вернет ссылку на JsonObject::invalid, после чего безопасно перейти к несуществующему дочернему элементу.

person mpromonet    schedule 30.08.2016