Search code examples
jsonluacjson

How to Efficiently Map Nested JSON to Custom Lua Data Structures in a REST API Client?


I'm working on a REST API client in Lua that consumes data from a third-party service. The service returns deeply nested JSON objects, and I need to map these to more simplified Lua tables for easier manipulation and data analysis. I've explored using libraries like cjson for basic deserialization, but I'm struggling with mapping the nested JSON to my custom data structures efficiently.

Here's a simplified example of the JSON object returned by the API:

{
  "data": {
    "id": 1,
    "attributes": {
      "name": "John",
      "details": {
        "age": 30,
        "address": {
          "street": "123 Main St",
          "city": "Anytown"
        }
      }
    }
  }
}

And here's what I would like to map this to in Lua:

Person = {
  id = 1,
  name = "John",
  age = 30,
  street_address = "123 Main St",
  city = "Anytown"
}

Is there a best practice for this type of mapping? Are there any Lua libraries that can assist with this, or should I be writing custom mapping functions? I'm concerned about both code maintainability and performance.

Thank you in advance for any insights or recommendations!


Solution

  • The "traversing JSON" functionality is useful here.

    local json = require"json"
    local s = [[
    {
       "data": {
          "id": 1,
          "attributes": {
             "name": "John",
             "details": {
                "age": 30,
                "address": {
                   "street": "123 Main St",
                   "city": "Anytown"
                }
             }
          }
       }
    }
    ]]
    local Person = {}
    
    json.traverse(s,
       function (path, json_type, value, pos, pos_last)
          local path_str = table.concat(path, "/")
          print("DEBUG:", path_str, json_type, value, pos, pos_last)
          if json_type == "number" or json_type == "string" then
             Person[path[#path]] = value
          end
       end
    )
    
    print"RESULT"
    for k,v in pairs(Person) do
       print(k, v)
    end
    

    Output:

    DEBUG:                                          object  nil          1    nil
    DEBUG:  data                                    object  nil          14   nil
    DEBUG:  data/id                                 number  1            28   28
    DEBUG:  data/attributes                         object  nil          51   nil
    DEBUG:  data/attributes/name                    string  John         70   75
    DEBUG:  data/attributes/details                 object  nil          98   nil
    DEBUG:  data/attributes/details/age             number  30           119  120
    DEBUG:  data/attributes/details/address         object  nil          146  nil
    DEBUG:  data/attributes/details/address/street  string  123 Main St  173  185
    DEBUG:  data/attributes/details/address/city    string  Anytown      211  219
    RESULT
    name    John
    street  123 Main St
    city    Anytown
    age     30
    id      1
    

    The library is here