Search code examples
karate

How to match dynamic key in nested json object in karate?


Trying to match nested json object from API response in Karate framework. Following in my API response.

    "status": {
        "code": 0,
        "message": "Successful"
    },
    "data": {
        "PHX": {
            "name": "Phoenix (All)",
            "city_code": "PHX",
            "country_code": "US",
            "country_name": "United States",
            "city_name": "Phoenix",
            "type": "city",
            "airports": {
                "SCF": {
                    "name": "Scottsdale Municipalcipal",
                    "city_code": "PHX",
                    "city_name": "Phoenix",
                    "country_code": "US",
                    "country_name": "United States",
                    "lat": 33.61667,
                    "lng": -111.916664,
                    "timezone": "America/Phoenix",
                    "type": "airport"
                },
                "PHX": {
                    "name": "Sky Harbor International",
                    "city_code": "PHX",
                    "city_name": "Phoenix",
                    "country_code": "US",
                    "country_name": "United States",
                    "lat": 33.435036,
                    "lng": -112.00016,
                    "timezone": "America/Phoenix",
                    "type": "airport"
                },
                "AZA": {
                    "name": "Williams Gateway",
                    "city_code": "PHX",
                    "city_name": "Phoenix",
                    "country_code": "US",
                    "country_name": "United States",
                    "lat": 33.307777,
                    "lng": -111.655556,
                    "timezone": "America/Phoenix",
                    "type": "airport"
                },
                "DVT": {
                    "name": "Phoenix Deer Valley Airport",
                    "city_code": "PHX",
                    "city_name": "Phoenix",
                    "country_code": "US",
                    "country_name": "United States",
                    "lat": 33.683887,
                    "lng": -112.083336,
                    "timezone": "America/Phoenix",
                    "type": "airport"
                },
                "LUF": {
                    "name": "Luke AFB",
                    "city_code": "PHX",
                    "city_name": "Phoenix",
                    "country_code": "US",
                    "country_name": "United States",
                    "lat": 33.61667,
                    "lng": -111.916664,
                    "timezone": "America/Phoenix",
                    "type": "airport"
                }
            }
        }
    }
}

I'm trying to match json object in data, here very tricky part is key "PHX" will be dynamic and I may get as many as json nested objects. Also the airports nested json like "SCF", "PHX", "AZA" are also dynamic. How can I write match statement in Karate? Tried several answers for dynamic keys but my API response is quite different here.

I'm tried below changes in feature file:

 * def response_data_airports =  { "#string": { "name": "#string", "city_code": "#string", "city_name": "#string", "country_code": "#string", "country_name": "#string", "lat": "#number", "lng": "#number", "timezone": "#string", "type": "#string" }}
 * def main_schema = { '#string': { "name": "#string", "city_code":
   "#string", "country_code": "#string", "country_name": "#string",
   "city_name": "#string", "type": "#string", "airports":
   '#(^response_data_airports)' }  }
 * match flightAutocompleteDetails.response.data contains main_schema

Thank you


Solution

  • Yes, this JSON is not easy to tackle. So you have to do a transform. I am focusing only on the response.data.PHX.airports part, the rest is a home-work for you ;)

    * def data = []
    * karate.forEach(response.data.PHX.airports, (k, v) => { v.code = k; data.push(v) })
    

    EDIT: since you weren't happy with my first answer, here is how you can avoid hard-coding PHX, and I can give you hints, you have to do what is right for your situation. Read other examples, there are many ways to "unpack" keys:

    * def data = []
    * def code = karate.keysOf(response.data)[0]
    * karate.forEach(response.data[code].airports, (k, v) => { v.code = k; data.push(v) })
    

    If you need more help to understand this, refer: https://github.com/karatelabs/karate#json-transforms

    What this does is gives you a neat array and also takes the key and adds it to each array item as a code property. Now you can easily do all the Karate matches you want.

    * match data[*].code == ['SCF', 'PHX', 'AZA', 'DVT', 'LUF']
    

    And since you seem to be stressing over doing a "schema" match, sure:

    * match each data contains { name: '#string', city_code: '#string' }
    

    My advice for testing is always test the whole payload, even if it feels like you are "hard coding" data. That is more useful to test whether your system is working correctly (instead of just schema matching) in my honest opinion.

    Also see: https://stackoverflow.com/a/57929311/143475