Anyway to loop through json array to ensure the array contains a key value pair?

5,299 views
Skip to first unread message

Kevin G

unread,
Sep 27, 2016, 5:36:24 PM9/27/16
to robotframework-users
if I have an json array object like this:
"scores": [
        {
          "id": 1,
          "value": "100"
        },
        {
          "id": 2,
          "value": "77"
        },
                   {
          "id": 3,
          "value": "96"
        }
]
}


My intention is to verify that the array in my json contains a value on a certain id.    
So in the json array above I want to make sure that somewhere in the array I have a score of value "77" for the id of "2".
In robot I know there are should contain built in keywords to do this but I not sure how to match on both id and value.

So I thought the best way to do it would be to loop through and check both the id and value.
How can I loop through the array of scores with the 2 fields in it and grab only the value from it?  
I tried using the for loop below and when i print out the score for each item I get the whole array printed out every time and not each item in the loop.
I looked at the examples and none have shown how to loop through each item object in the json array correctly. 

${testScores} = json array example above  i.e. data.json()

:FOR    ${score}  in    ${testScores['scores']} 
    \    log    ${score['value']}


Can anyone point me in the right direction?  Thanks

J Wipf

unread,
Sep 28, 2016, 6:59:29 AM9/28/16
to robotframework-users
I haven't specifically encountered this before, but based on this snippet:
... I get the whole array printed out ...
I'm guessing you could use that information and the Create Dictionary keyword [1] to create a RF dictionary. Afterwards I believe if you use the Dictionary Should Contain Item keyword it would do what you're looking for.

Zeke Fiddler

unread,
Sep 28, 2016, 12:59:17 PM9/28/16
to robotframework-users
Hi Kevin,

The Builtin keyword "Should Contain" will easily assert that your desired pair is somewhere within your JSON.  

If you want to get more granular and make certain you are finding the value pair within the embedded array, then you might want to import HttpLibrary(livetest). You will find some very useful keywords for fussing with JSON within that library.

Then the simple solution to your particular question would be to build a test case using the keyword "Get Json Value"  from this library and assert that your pair exists using the "Should Contain" keyword to assert that your pair is present. 

| A Simple Example of "Get Json Value" and "Should Contain" Keywords |
|     | ${scores}= | Get Json Value | ${testscores} | /scores |
|     | Should Contain | ${scores} | {"id": 2, "value": "77"} |



If that adequately solves your problem then the following may be TLDR

However, if you want to approach it the way you described in your question by using a for loop -- which might make sense if you are dealing with a very large array of id and score pairs -- a possible solution might look something more like this, where we loop through the paris and exit once we find the desired result. The test will fail if the desired result is not found.


| Does Id 2 Have A Score Of 77? |
|     | ${parsed}= | Parse JSON | ${testscores} | # get the data structure of the json object
|     | ${length}= | get length | ${parsed["scores"]} | # this will be the duration of our for loop
|     | :FOR | ${score_pair} | In Range | 0 | ${length} | # looping through the values
|     |      | ${id_value_pair}= | Get JSON Value | ${testscores} | /scores/${score_pair} | # Gets the embedded array
|     |      | ${status}= | Run Keyword And Return Status | Should Be Equal As Strings | ${id_value_pair} | {"id": 2, "value": "77"} | # Asks if we Pass or Fail
|     |      | Exit For Loop IF | ${status} | # exits as soon as we find our desired result
|     | Should Be Equal As Strings | ${id_value_pair} | {"id": 2, "value": "77"} | | # asserts that we found the desired result


Here is a complete test file which you are welcome to copy and run in order to see these test cases in action:


| *** Settings *** |
| Documentation | Three examples of how one might assert that an array of objects within |
| ...           | a larger JSON object contains a certain combination of values. |
| ...           | Requires HTTPLibrary which you can install with pip install --upgrade robotframework-httplibrary |


| Library | HttpLibrary.HTTP |
| Force Tags | temp |


| *** Variables *** |


| ${testscores} | {"scores": [{"id": 1, "value": "100"}, {"id": 2, "value": "77"}, {"id": 3, "value": "96"} ] } |




| *** Test Cases *** |

| Bare Bones Solution |
|     | Should Contain | ${testscores} | {"id": 2, "value": "77"} |


| A Simple Example of "Get Json Value" and "Should Contain" Keywords |
|     | ${scores}= | get json value | ${testscores} | /scores |
|     | Should Contain | ${scores} | {"id": 2, "value": "77"} |


| Does Id 2 Have A Score Of 77? |
|     | ${parsed}= | Parse JSON | ${testscores} | # get the data structure of the json object
|     | ${length}= | get length | ${parsed["scores"]} | # this will be the duration of our for loop
|     | :FOR | ${score_pair} | In Range | 0 | ${length} | # looping through the values
|     |      | ${id_value_pair}= | Get JSON Value | ${testscores} | /
scores/${score_pair} | # Gets the embedded array
|     |      | ${status}= | Run Keyword And Return Status | Should Be Equal As Strings | ${id_value_pair} | {"id": 2, "value": "77"} | # Asks if we Pass or Fail
|     |      | Exit For Loop IF | ${status} | # exits as soon as we find our desired result
|     | Should Be Equal As Strings | ${id_value_pair} | {"id": 2, "value": "77"} | | # asserts that we found the desired result


| Here Is The Same Operation Executed As A Keyword With Args |
|     | Does The Json Array Contain Our Desired Packet? | ${testscores} | scores | {"id": 2, "value": "77"} |


| *** Keywords *** |


| Does The Json Array Contain Our Desired Packet? |
|     | [Documentation] | Find a given JSON packet in an array of packets embedded within a JSON object |
|     | [Arguments] | ${our_json} | ${json_pointer} | ${expected_json_packet} |
|     | ${parsed}= | Parse JSON | ${our_json} |
|     | ${length}= | get length | ${parsed["${json_pointer}"]} |
|     | :FOR | ${our_pair} | In Range | 0 | ${length} |
|     |      | ${id_value_pair}= | Get JSON Value | ${our_json} | /${json_pointer}/${our_pair} |
|     |      | ${status}= | Run Keyword And Return Status | Should Be Equal As Strings | ${id_value_pair} | ${expected_json_packet} |
|     |      | Exit For Loop IF | ${status}
|     | Should Be Equal As Strings | ${id_value_pair} | {"id": 2, "value": "77"} |

I hope this helps.






NOTICE: This email may contain information that is confidential or attorney-client privileged and may constitute inside information or trade secrets. The contents of this email are intended only for the recipient(s) listed above. If you are not the intended recipient, you are directed not to read, disclose, distribute or otherwise use this transmission. If you have received this email in error, please notify the sender immediately and delete the transmission. Delivery of this message is not intended to waive any applicable privileges.

Kevin G

unread,
Sep 28, 2016, 3:31:44 PM9/28/16
to robotframe...@googlegroups.com
Thank you for the responses J Wipf and Zeke Fiddler!   I didn't want to complicate it anymore by creating additional objects like dictionaries and was looking for something more straight forward.

That was exactly what I am looking for Zeke.  I didn't realize that the HttpLibrary.Http had all those json keywords.  I was using the robotframework-requests library to make my API calls.  
I imported the HttpLibrary and was able to run your file and looks really good.  
I wanted to run the simple solution in my case since I get a response from an API call and need to pull out the json scores array from it, but for some reason it keeps failing with 

TypeError: expected string or buffer

when running the below code

|  ${scores}= |  GetJsonValue  |  ${response.json()}  |  /scores


But thank you Zeke, this is immensely helpful and will get me further along.  Thanks!

Edit: I was able to figure it out, I keep forgetting I need to deal with this in string form and not unicode.  Had to use the Stringify Json call to convert it to string before getting it and checking it. 
Much appreciated Zeke!
Reply all
Reply to author
Forward
0 new messages