2

I miserably fail to convert nested Lua table into C json object. I'm using LuaTableToJson (see code here after) where "index" is the parameter index to retrieve and PopOneArg the routine to process a value depending on its type.

When having a nested table I would like to call LuaTableToJson recursively, but with index=-1 it does not work.

Question: could please someone point on working sample to retreive nested table passed as argument from Lua to C. Alternatively could someone explain the stack structure when passing nested table from Lua to C.

Thank you

Note: for C->Lua I have a solution.

    STATIC json_object *LuaTableToJson (lua_State* luaState, int index) {
    int idx;

    json_object *tableJ= json_object_new_object();
    const char *key;
    char number[3];
    lua_pushnil(luaState); // 1st key
    for (idx=1; lua_next(luaState, index) != 0; idx++) {

        // uses 'key' (at index -2) and 'value' (at index -1)
        if (lua_type(luaState,-2) == LUA_TSTRING) key= lua_tostring(luaState, -2);
        else {
            snprintf(number, sizeof(number),"%d", idx);
            key=number;
        } 
        json_object *argJ= PopOneArg(luaState, -1);
        json_object_object_add(tableJ, key, argJ);
        lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration 
    } 

    // Query is empty free empty json object
    if (idx == 1) {
        json_object_put(tableJ);
        return NULL;
    }
    return tableJ;
}

STATIC  json_object *PopOneArg (lua_State* luaState, int idx) {
    json_object *value=NULL;

    int luaType = lua_type(luaState, idx);
    switch(luaType)  {
        case LUA_TNUMBER: {
            lua_Number number= lua_tonumber(luaState, idx);;
            int nombre = (int)number; // evil trick to determine wether n fits in an integer. (stolen from ltcl.c)
            if (number == nombre) {
                value= json_object_new_int((int)number);
            } else {
                value= json_object_new_double(number);
            }
            break;
        }
        case LUA_TBOOLEAN:
            value=  json_object_new_boolean(lua_toboolean(luaState, idx));
            break;
        case LUA_TSTRING:
           value=  json_object_new_string(lua_tostring(luaState, idx));
            break;
        case LUA_TTABLE: {
            if (idx > 0) {
                value= LuaTableToJson(luaState, idx);
            } else {
                value= json_object_new_string("UNSUPPORTED_Lua_Nested_Table");
            }
            break;                
        }    
        case LUA_TNIL:
            value=json_object_new_string("nil") ;
            break;

        default:
            AFB_NOTICE ("PopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType));
            value=NULL;
    }

    return value;    
}

static json_object *LuaPopArgs (lua_State* luaState, int start) {    
    json_object *responseJ;

    int stop = lua_gettop(luaState);
    if(stop-start <0) return NULL;

    // start at 2 because we are using a function array lib
    if (start == stop) {
        responseJ=PopOneArg (luaState, start);
    } else {
        // loop on remaining return arguments
        responseJ= json_object_new_array();
        for (int idx=start; idx <= stop; idx++) {
            json_object *argJ=PopOneArg (luaState, idx);
            if (!argJ) goto OnErrorExit;
            json_object_array_add(responseJ, argJ);     
       }
    }

    return responseJ;

  OnErrorExit:
    return NULL;
}   
Marc Balmer
  • 1,780
  • 1
  • 11
  • 18
Fulup
  • 545
  • 3
  • 14
  • 1
    `lua_next`, integer keys, and `lua_tostring` don't mix well, check the [manual](http://www.lua.org/manual/5.2/manual.html#lua_tolstring) and this [SO question](https://stackoverflow.com/questions/29287988/iterating-over-table-of-tables-with-the-lua-c-api/29288604#29288604) – Adam Aug 15 '17 at 18:31
  • Will `number` ever be over `99` in length? If so, create a bigger array. – ryyker Aug 15 '17 at 18:38
  • I do not understand you comment. lua_string work well, when properly handle. For the 99 and do not even understand the relation with the problem. What the relation with importing nested table ? – Fulup Aug 15 '17 at 18:58

1 Answers1

1

For a packaged solution, check my code here

Essentially, when parsing a nested table, you should be parsing at a negative index. In this case, lua_next will mess up the stack relative to the index you're tracking, so you need to decrement it.

You can try

if (index < 0) index--; // change to -- as in your code

When parsing tables. It should work with your code, but I can't promise there's not another problem I'm missing.

My code works for sure to any level of nesting, so I'd recommend following it as an example and posting your final code as an answer once it works.

Fulup
  • 545
  • 3
  • 14
Nick Cano
  • 437
  • 2
  • 7
  • In fact checking for your code it was if (index < 0) index--; that was needed. Thank you very much without your help. Without your proposition I would never have not found the trick. My code is accessible from github.com/iotbzh/audio-bindings/blob/master/Controller-afb/… . From the code I previously post, the only change is than before the loop with lua_next(luaState, index) I needed to add if (index < 0) index--; – Fulup yesterday – Fulup Aug 18 '17 at 07:22