Parsing Json in Robot Framework - json

{
"data": [
{
"name": "John",
"mobile_phone": false,
"carrier": "none"
},
{
"name": "Jim",
"mobile_phone": true,
"carrier": "T-Mobile"
}
],
"result": 0
}
Hi, will it be possible to parse such JSON response in Robot Framework in a way where I will create kind of 'sub' list for each value ?
I would like to separate John from Jim and get for example information about carrier for Jim only (via another get request later in test).
Thanks !

Say the source text (the json) is stored in a variable ${source data}:
${source data}= Evaluate json.loads("""${source data}""") json
# the variable ${source data} is now a python dictionary - the same as the original json, but only - accessible as dictionary in robotframwork
${all data members}= Set Variable ${source data['data']}
${user_phone}= Create Dictionary
:FOR ${member} IN #{all data members} # iterate through the 'data', each ${member} is a dictionary in the source list
\ ${name}= Get From Dictionary ${member} name # will assign to the variable ${name} the value of the key 'name'; if there is no such key - the keyword will fail
\ Log The user ${name} has a mobile phone: ${member['mobile_phone']} # Will print "The user John has a mobile phone: False", "The user Jim has a mobile phone: True"
\ Set To Dictionary ${user_phone} ${name} ${member['mobile_phone']} # will fill-in a dictionary in the form "name": boolean_does_the_person_has_phone
This commented code sample shows how you can work with json/dictionary objects in robotframework.
The Evaluate keyword on line 1 runs arbitrary python code (its first argument, which calls the loads() method of the json module); its 2nd argument is any extra libraries that need to be imported - like json in our case.
The 4th line, Set Variable shows the Extended variable syntax - in this case, knowing that source data is a dictionary, getting the value of that key. At the end of this line execution, the variable all data members is the list that's inside the json's 'data' key.
Line 8 starts a loop, over that same list; the variable member will hold the value of each list's member on every iteration.
Line 9 uses a different (more orthodox) way to get the value of a dictionary's key - by using the keyword Get From Dictionary from the Collections library.
Line 10 logs a message, by employing a normal (name) and extended syntax (member['mobile_phone']) variables.
And on line 11, a dictionary entry is created, where the name is used as a key, and the boolean member['mobile_phone'] as a value (if there is already a key with the same name - it is overwritten). This keyword is again in the Collections library.

Related

Elasticsearch 8.0.0 mapper_parsing_exception of a string literal for field type "flattened"

I have a Problem to insert a document via Api to my ES 8.0.0.
In my IndexTemplate I defined a mapping of a property called [Data] of type "flattend".
For "normal" JSON-Objects it works fine. But when I try to insert a plain string literal (for example "test") or a number (for example 4) I get a "400 Bad Request". JSONLint says it's a valid JSON!!
{
....
"Data": "test",
....
}
Can i configure ES to accept such kind of JSON for type "flattened"??
As Elasticsearch document mentions:
The flattened type provides an alternative approach, where the entire
object is mapped as a single field. Given an object, the flattened
mapping will parse out its leaf values and index them into one field
as keywords.
So, the value provided for the "flattened" field type should be a JsonObject.
Hence, below works as where "full_name" is of type "flattened"
"full_name":{
"name":"nishikant"
}
But below does not
"full_name":"nishikant".
Same has been given in exception
"reason" : "Failed to parse object: expecting token of type [START_OBJECT] but found [VALUE_STRING]"

Azure Data Factory - forEach - JSON to array errors

My aim is to use Azure Data Factory to copy data from one place to another using REST API.
The first part of the copying is using the ForEach activity to select parameters from a nested JSON/array. I have had problems with this nested JSON because of various error messages in the ForEach activity.
My JSON is of the following form:
(
{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}
)
and first I'm setting the #json conversion function in front of it:
#json(
'{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}'
)
Here you can see the Execute Pipeline object and its parameters:
I am setting this JSON as a parameter in the Execute Pipeline object. I am setting its type as an "Array". (For the record, I have still had similar error messages even though I have tried to change the parameter type to "String" or "Object".)
The ForEach activity is used to select an item from the nested JSON, which is written into a parameter.
This immediately produces the following error in the Execute Pipeline activity:
Operation on target... ...failed: The function 'length' expects its parameter to be an array or a string. The provided value is of type 'Object'.
So, even though I set the JSON parameter type to "Array", it is changed into "Object" when I debug the pipeline activity. See the error below:
Next, I tried to use the ADF #createArray function before my JSON text.
#createArray(
'{
"key_1": "value_1",
"key_2": [
"value_2_1",
"value_2_2"
]
}'
)
When debugging, the forEach activity throws me an error in the first Copy data activity:
The expression 'concat(item().SELECTING_key_1_FROM_MY_JSON))' cannot be evaluated because property 'key_1' cannot be selected. Property selection is not supported on values of type 'String'.
Please help me, what I am doing wrong in my attempts of converting the JSON to an array? What should I change in my code?
So far I have tried changing the parameter type and using various functions in the JSON dynamic content but with no luck.
update:
If you want change the filename in Copy Data --> Sink.
You can key in the dynamic content #concat(pipeline().parameters.Pip_Object.key_1,'.json') to rename the file.
Please correct me if I understand you wrong.
First, we should use Parameters to store the Json array as follows. Because Variables are not support to store the Json array. Variables are only support to store simple data type such as ["1","2","3"...].
Json array format should as follows:
[{"key_1": "value_1"},{"key_2": ["value_2_1","value_2_2"]}]
I created a simple test here. Then ForEach the Json array:
Inside ForEach1 activity, pass #item() to the object type parameter Pip_Object.
The Input is as follows:
Add dynamic content above using any combination of expressions, functions and system variables at for each settings
#activity('Get Metadata1').output.childItems
We ended up using two pipelines: a generic one and a specific one.
The generic one has the JSON as a parameter (JSON_PARAMETER), and we set it a default value in the following form
[{"key_1":"value_1","key_2":["value_2"]},{"key_1":"value_3","key_2":["value_2"]}, ...etc. ...}]
The generic pipeline has a forEach loop, in which that mentioned JSON parameter is called in Settings -> Items:
#JSON(pipeline().parameters.JSON_PARAMETER)
In the specific pipeline, there is an Execute Pipeline activity, where the JSON_PARAMETER is found in Settings -> Parameters. If the default value of JSON_PARAMETER is used, the field is left blank. If we want to change the parameter, before Execute Pipeline, we put a Set Variable activity where we change the Variables -> Value to:
#concat('
[
{"key_1":"value_1",
"key_2":
["value_2",
"value_3"
]
},
{"key_1":"value_3",
...and so on...
}
]
')

Get multiple JSON keypair values within a JSON object only if a specific keypair value matches iterable

I am trying to pull multiple json keyvalue data within any given JSON object, from a JSON file produced via an API call, for only the JSON objects that contain a specific value within a keyvalue pair; this specific value within a keyvalue pair is obtained via iterating through a list. Hard to articulate, so let me show you:
Here is a list (fake list representing my real list) I have containing the "specific value within a keyvalue pair" I'm interested in:
mylist = ['device1', 'device2', 'device3']
And here is the json file (significantly abridged, obviously) that I am reading from my python script:
[
{
"name": "device12345",
"type": "other",
"os_name": "Microsoft Windows 10 Enterprise",
"os_version": null
},
{
"name": "device2",
"type": "other",
"os_name": "Linux",
"os_version": null
},
{
"name": "device67890",
"type": "virtual",
"os_name": "Microsoft Windows Server 2012 R2 Standard",
"os_version": null
}
]
I want to iterate through mylist, and for each item in mylist that matches the json keyvalue key "name" in the json file, print the values for 'name' and 'os_name'. In this case, I should see a result where after iterating through mylist, device2 matches the 'name' key in the json object that contains {'name':'device2'}, and then prints BOTH the 'name' value ('device2') and the 'os_name' value ('Linux'), and then continues iterating through mylist for the next value to iterate through the json file.
My code that does not work; let us assume the json file was opened correctly in python and is defined as my_json_file with type List:
for i in mylist:
for key in my_json_file:
if key == i:
deviceName = i.get('name')
deviceOS = i.get('os_name')
print(deviceName)
print(deviceOS)
I think the problem here is that I can't figure out how to "identify" json objects that "match" items from mylist, since the json objects are "flat" (i.e. 'device2' in my json file doesn't have 'name' and 'os_name' nested under it, which would allow me to dict.get('device2') and then retrieve nested data).
Sorry if I did not understand your question clearly, but it appears you're trying to read values off of the list instead of the JSON file since you're looking at i.get instead of key.get when key is what actually contains the information.
And for the optimization issue, I'd recommend converting your list of devices into a set. You can then iterate through the JSON array and check if a given name is in the set instead of doing it the other way. The advantage is that sets can return if they contain an item in O(1) time, meaning it will significantly speed up the overall speed of the program to O(n) where n = size of json array.
for key in my_json_file:
if key.get('name') in my_list:
deviceName = key.get('name')
deviceOS = key.get('os_name')
print(deviceName)
print(deviceOS)

How to pass arguments directly from json file to keyword in robot framework?

I have a Json file like this.PFB the code:
"properties " : {
"xyz" : {
"username" : "abc#gmail.com",
"password" : "abc#123",
"phonenumber" : "1235",
},
"ABC" : {
"username" : "abc#gmail.com",
"password" : "abc#123",
"phonenumber" : "1345",
},
Keyword will be like :
Do operation for properties
[Arguments] ${username} ${password} ${phonenumber}
Log ${username}
Log ${password}
Log ${phonenumber}
My questions are :
1) The json file contains so many things but i have to fetch only properties from the file.How i will take properties part from the entire json file and pass directly arguments like username , password , phonenumber into keyword mentioned above.
2) How to write keyword for this logic such that we change only json file for adding more properties like apart from xyz, abc we will add as many properties we want and it will automatically fetch and keyword give us the desired result for all properties whatever we are modifying in json file.
If I understand you correctly, your question's summary is: a) how to read and parse a json file in Robotframework in this form, and b) pass each record's attributes to this keyword.
A file can be read from the file system with Get File.
One can read a json file with the python's json module, more specifically the loads() method - it takes a string, and returns a python object.
Your "json" sample is quite invalid json, so let's imagine that "properties" is somewhere (3 levels deep) inside the file.
${the file as string}= Get File c:\\the\\path\\to\\the\\file.json
${parsed}= Evaluate json.loads("""${the file as string}""") json
${properties}= Set Variable ${parsed["top"]["child"]["properties"]}
And now the variable properties is a dictionary, with those two keys - "ABC" and "xyz"; you just iterate over it, and pass the subkeys of each of the sub-dictionaries to the keyword.
FOR ${key} IN #{properties}
${sub dict}= Get From Dictionary ${properties} ${key}
Do operation for properties ${sub dict}[username] ${sub dict}[password] ${sub dict}[phonenumber]
END

Json handling in ROBOT

I have a Json file in which there is a field which I need to edit and save the file for next usage.
But the field which I need to edit is as shown below,
The value I need to assign fr the field is generated Randomly in run time which i'll be capturing in a variable and pass it to this json specific key "dp" then save the json.
The saved json will be used for REST POST url.
{
"p": "10",
"v": 100,
"vt": [
{
"dp": "Field to be edited"(integer value) ,
]
}
The simplest solution would be to write a python keyword that can change the value for you. However, you can solve this with robot keywords by performing the following steps:
convert the JSON string to a dictionary
modify the dictionary
convert the dictionary back to a JSON string
Convert the JSON string to a dictionary
Python has a module (json) for working with JSON data. You can use the evaluate keyword to convert your JSON string to a python dictionary using the loads (load string) method of that module.
Assuming your JSON data is in a robot variable named ${json_string}, you can convert it to a python dictionary like this:
${json}= evaluate json.loads('''${json_string}''') json
With the above, ${json} now holds a reference to a dictionary that contains all of the json data.
Modify the dictionary
The Collections library that comes with robot has a keyword named set to dictionary which can be used to set the value of a dictionary element. In this case, you need to change the value of a dictionary nested inside the vt element of the JSON object. We can reference that nested dictionary using robot's extended variable syntax.
For example:
set to dictionary ${json["vt"]} dp=the new value
With that, ${json} now has the new value. However, it is still a python dictionary rather than JSON data, so there's one more step.
Convert the dictionary back to JSON
Converting the dictionary back to JSON is the reverse of the first step. Namely, use the dumps (dump string) method of the json module:
${json_string}= evaluate json.dumps(${json}) json
With that, ${json_string} will contain a valid JSON string with the modified data.
Complete example
The following is a complete working example. The JSON string will be printed before and after the substitution of the new value:
*** Settings ***
Library Collections
*** Test Cases ***
Example
${json_string}= catenate
... {
... "p": "10",
... "v": 100,
... "vt": {
... "dp": "Field to be edited"
... }
... }
log to console \nOriginal JSON:\n${json_string}
${json}= evaluate json.loads('''${json_string}''') json
set to dictionary ${json["vt"]} dp=the new value
${json_string}= evaluate json.dumps(${json}) json
log to console \nNew JSON string:\n${json_string}
For reading and writing data to and from file I am using OperatingSystem library
${json} Get Binary File ${json_path}nameOfJsonFile.json
It works for me on API testing, to read .json and POST, like here
*** Settings ***
Library Collections
Library ExtendedRequestsLibrary
Library OperatingSystem
*** Variables ***
${uri} https://blabla.com/service/
${json_path} C:/home/user/project/src/json/
*** Test Cases ***
Robot Test Case
Create Session alias ${uri}
&{headers} Create Dictionary Content-Type=application/json; charset=utf-8
${json} Get Binary File ${json_path}nameOfJsonFile.json
${resp} Post Request alias data=${json} headers=${headers}
Should Be Equal As Strings ${resp.status_code} 200
For integer values in JSON, the other answers did not work for me.
This worked:
${json}= Catenate { "p": "10", "v": 100, "vt": { "dp": "Field to be edited" } }
${value} Set Variable 2 #the value you want
${value} Convert To Integer ${value}
${json}= Evaluate json.loads('''${json}''') json
Set To Dictionary ${json["vt"]} dp=${value}
${json}= Evaluate json.dumps(${json}) json
Log ${json}
Convert To Integer was required, otherwise the value is still in string "${value}"