Skip to content

Commit 67e1c9e

Browse files
authored
Merge pull request #35 from elrandira/master
Fixes for #5, #6, #25, #26, #29, #30
2 parents 80fecd2 + f0cb50e commit 67e1c9e

File tree

9 files changed

+215
-33
lines changed

9 files changed

+215
-33
lines changed

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,17 @@ dist
77
build
88
htmlcov
99
result
10+
11+
# PyDev/RED
12+
.settings
13+
.project
14+
.pydevproject
15+
__pycache__
16+
red.xml
17+
/libspecs/
18+
/Results/
19+
20+
#pipenv/virtualenv
21+
.venv
22+
Pipfile
23+
Pipfile.lock

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: python
22
python:
3-
- "2.7"
3+
- "3.7"
44
install: "pip install -r requirements.txt"
55
script: make clean coverage docs dist
66
deploy:

JSONLibrary/JSONLibraryKeywords.py

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# -*- coding: utf-8 -*-
2+
import io
23
import json
34
import os.path
45
from robot.api import logger
56
from robot.api.deco import keyword
6-
from jsonpath_rw import Index, Fields
7-
from jsonpath_rw_ext import parse
7+
from robot.utils.asserts import assert_true, fail
8+
from jsonpath_ng import Index, Fields
9+
from jsonpath_ng.ext import parse
810
from .version import VERSION
911

1012
__author__ = 'Traitanit Huangsri'
@@ -16,13 +18,14 @@ class JSONLibraryKeywords(object):
1618
ROBOT_EXIT_ON_FAILURE = True
1719

1820
@keyword('Load JSON From File')
19-
def load_json_from_file(self, file_name):
21+
def load_json_from_file(self, file_name, encoding=None):
2022
"""Load JSON from file.
2123
2224
Return json as a dictionary object.
2325
2426
Arguments:
2527
- file_name: absolute json file name
28+
- encoding: encoding of the file
2629
2730
Return json object (list or dictionary)
2831
@@ -33,7 +36,7 @@ def load_json_from_file(self, file_name):
3336
if os.path.isfile(file_name) is False:
3437
logger.error("JSON file: " + file_name + " not found")
3538
raise IOError
36-
with open(file_name) as json_file:
39+
with io.open(file_name,mode='r',encoding=encoding) as json_file:
3740
data = json.load(json_file)
3841
return data
3942

@@ -53,11 +56,23 @@ def add_object_to_json(self, json_object, json_path, object_to_add):
5356
| ${json}= | Add Object To Json | ${json} | $..address | ${dict} |
5457
"""
5558
json_path_expr = parse(json_path)
56-
for match in json_path_expr.find(json_object):
57-
if type(match.value) is dict:
58-
match.value.update(object_to_add)
59-
if type(match.value) is list:
60-
match.value.append(object_to_add)
59+
rv=json_path_expr.find(json_object)
60+
if len(rv):
61+
for match in rv:
62+
if type(match.value) is dict:
63+
match.value.update(object_to_add)
64+
if type(match.value) is list:
65+
match.value.append(object_to_add)
66+
else:
67+
parent_json_path='.'.join(json_path.split('.')[:-1])
68+
child_name=json_path.split('.')[-1]
69+
json_path_expr = parse(parent_json_path)
70+
rv=json_path_expr.find(json_object)
71+
if len(rv):
72+
for match in rv:
73+
match.value.update({child_name:object_to_add})
74+
else:
75+
fail(f"no match found for parent {parent_json_path}")
6176

6277
return json_object
6378

@@ -75,7 +90,11 @@ def get_value_from_json(self, json_object, json_path):
7590
| ${values}= | Get Value From Json | ${json} | $..phone_number |
7691
"""
7792
json_path_expr = parse(json_path)
78-
return [match.value for match in json_path_expr.find(json_object)]
93+
rv=json_path_expr.find(json_object)
94+
# make the keyword fails if nothing was return
95+
assert_true(rv is not None and len(rv)!=0,
96+
f"Get Value From Json keyword failed to find a value for {json_path}")
97+
return [match.value for match in rv]
7998

8099
@keyword('Update Value To Json')
81100
def update_value_to_json(self, json_object, json_path, new_value):
@@ -114,7 +133,7 @@ def delete_object_from_json(self, json_object, json_path):
114133
| ${json_object}= | Delete Object From Json | ${json} | $..address.streetAddress |
115134
"""
116135
json_path_expr = parse(json_path)
117-
for match in json_path_expr.find(json_object):
136+
for match in reversed(json_path_expr.find(json_object)):
118137
path = match.path
119138
if isinstance(path, Index):
120139
del(match.context.value[match.path.index])
@@ -136,7 +155,7 @@ def convert_json_to_string(self, json_object):
136155
"""
137156
return json.dumps(json_object)
138157

139-
@keyword('Convert String to JSON')
158+
@keyword('Convert String To JSON')
140159
def convert_string_to_json(self, json_string):
141160
"""Convert String to JSON object
142161
@@ -150,3 +169,60 @@ def convert_string_to_json(self, json_string):
150169
"""
151170
return json.loads(json_string)
152171

172+
@keyword('Dump JSON To File')
173+
def dump_json_to_file(self, dest_file, json_object):
174+
"""Dump JSON to file
175+
176+
Arguments:
177+
- dest_file: destination file
178+
- json_object: json as a dictionary object.
179+
180+
Export the JSON object to a file
181+
182+
Examples:
183+
| Dump JSON To File | ${OUTPUTID)${/}output.json | ${json} |
184+
"""
185+
json_str = self.convert_json_to_string(json_object)
186+
with open(dest_file, "w") as json_file:
187+
json_file.write(json_str)
188+
return str(dest_file)
189+
190+
@keyword('Should Have Value In Json')
191+
def should_have_value_in_json(self, json_object, json_path):
192+
"""Should Have Value In JSON using JSONPath
193+
194+
Arguments:
195+
- json_object: json as a dictionary object.
196+
- json_path: jsonpath expression
197+
198+
Fail if no value is found
199+
200+
Examples:
201+
| Should Have Value In Json | ${json} | $..id_card_number |
202+
"""
203+
try:
204+
self.get_value_from_json(json_object, json_path)
205+
except AssertionError:
206+
fail(f"No value found for path {json_path}")
207+
208+
209+
@keyword('Should Not Have Value In Json')
210+
def should_not_have_value_in_json(self, json_object, json_path):
211+
"""Should Not Have Value In JSON using JSONPath
212+
213+
Arguments:
214+
- json_object: json as a dictionary object.
215+
- json_path: jsonpath expression
216+
217+
Fail if at least one value is found
218+
219+
Examples:
220+
| Should Not Have Value In Json | ${json} | $..id_card_number |
221+
"""
222+
try:
223+
rv=self.get_value_from_json(json_object, json_path)
224+
except AssertionError:
225+
pass
226+
else:
227+
fail(f"Match found for parent {json_path}: {rv}")
228+

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,20 @@ ${value}= | Get Value From Json | ${json_obj} | $..country |
3737
|${value_to_update}=| Set Variable | Japan | | |
3838
|${json_obj}= | Update Value To Json | ${json_obj} | $..country | ${value_to_update}|
3939
|Should Be Equal As Strings | ${json_obj['country'] | ${value_to_update} | | |
40+
|Should Have Value In Json | ${json_obj} | $..isMarried |
41+
|Should Not Have Value In Json | ${json_obj} | $..hasSiblings |
42+
|Dump JSON To File | ${OUTPUTID)${/}output.json | ${json} |
43+
4044

4145
# Documentation
4246
For the detail keyword documentation. Go to this following link:
4347

4448
https://robotframework-thailand.github.io/robotframework-jsonlibrary/
4549

50+
For an example of JSONPath expressions. Go to this link:
51+
52+
https://goessner.net/articles/JsonPath/index.html#e3
53+
4654
#Help & Issues
4755
Mention me on Twitter [@nottyo](https://twitter.com/nottyo)
4856

acceptance/JSONLibrary.robot

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Library JSONLibrary
33
Library Collections
44
Library String
5+
Library OperatingSystem
56
Test Setup SetUp Test
67
Default Tags JSONLibrary
78

@@ -17,24 +18,54 @@ TestAddJSONObjectByJSONPath
1718
${json_obj}= Add Object To Json ${json_obj} $..address ${object_to_add}
1819
Dictionary Should Contain Sub Dictionary ${json_obj['address']} ${object_to_add}
1920

21+
${json_obj}= Add Object To Json ${json_obj} $.friends ${None}
22+
Dictionary Should Contain Key ${json_obj} friends
23+
2024
TestGetValueByJSONPath
2125
[Documentation] Get some json object using JSONPath
2226
${values}= Get Value From Json ${json_obj} $..address.postalCode
2327
Should Be Equal As Strings ${values[0]} 630-0192
2428

29+
TestErrorGetValueByJSONPath
30+
[Documentation] Check Get Value From JSON can fail if no match is found
31+
Run Keyword And Expect Error *failed to find* Get Value From Json ${json_obj} $..errorField
32+
2533
TestUpdateValueByJSONPath
2634
[Documentation] Update value to json object using JSONPath
2735
${json_obj}= Update Value To Json ${json_obj} $..address.city Bangkok
2836
${updated_city}= Get Value From Json ${json_obj} $..address.city
2937
Should Be Equal As Strings ${updated_city[0]} Bangkok
3038

39+
TestShouldHaveValueByJSONPath
40+
[Documentation] Check a value can be found in json object using JSONPath
41+
Should Have Value In Json ${json_obj} $..isMarried
42+
43+
Run Keyword And Expect Error *No value found* Should Have Value In Json ${json_obj} $..hasSiblings
44+
45+
TestShouldNotHaveValueByJSONPath
46+
[Documentation] Check a value cannot be found in json object using JSONPath
47+
Should Not Have Value In Json ${json_obj} $..hasSiblings
48+
49+
Run Keyword And Expect Error *Match found* Should Not Have Value In Json ${json_obj} $..isMarried
50+
3151
TestDeleteObjectByJSONPath
3252
[Documentation] Delete object from json object using JSONPath
3353
${json_obj}= Delete Object From Json ${json_obj} $..isMarried
3454
Dictionary Should Not Contain Key ${json_obj} isMarried
3555

56+
TestDeleteArrayElementsByJSONPath
57+
[Documentation] Delete array elements from json object using JSONPath
58+
${json_obj}= Delete Object From Json ${json_obj} $..phoneNumbers[0]
59+
Length Should Be ${json_obj['phoneNumbers']} 2
60+
${json_obj}= Delete Object From Json ${json_obj} $..phoneNumbers[*]
61+
Length Should Be ${json_obj['phoneNumbers']} 0
62+
3663
TestConvertJSONToString
3764
[Documentation] Convert JSON To String
3865
${json_str}= Convert JSON To String ${json_obj}
3966
Should Be String ${json_str}
4067

68+
TestDumpJSONToFile
69+
[Documentation] Dumps JSON to file
70+
Dump JSON to file ${TEMPDIR}/sample_dump.json ${json_obj}
71+
File Should Exist ${TEMPDIR}/sample_dump.json

requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
robotframework>=3.0
2-
jsonpath-rw==1.4.0
3-
jsonpath-rw-ext>=0.1.9
2+
jsonpath-ng>=1.4.3
43
coverage==4.2

setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
VERSION='0.1' #
1515

1616
requirements = [
17-
'tox==3.0.0',
17+
'tox>=3.0.0',
1818
'coverage',
1919
'robotframework>=3.0',
20-
'jsonpath-rw==1.4.0',
21-
'jsonpath-rw-ext>=0.1.9'
20+
'jsonpath-ng>=1.4.3'
2221
]
2322

2423
test_requirements = [

tests/json/example.json

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@
1212
"city": "Nara",
1313
"postalCode": "630-0192"
1414
},
15-
"phoneNumbers": [{
16-
"type": "iPhone",
17-
"number": "0123-4567-8888"
18-
}, {
19-
"type": "home",
20-
"number": "0123-4567-8910"
21-
}]
15+
"phoneNumbers": [
16+
{
17+
"type": "iPhone",
18+
"number": "0123-4567-8888"
19+
}, {
20+
"type": "home",
21+
"number": "0123-4567-8910"
22+
}, {
23+
"type": "car",
24+
"number": "0123-4567-8999"
25+
}
26+
],
27+
"siblings": [],
28+
"occupation": null
2229
}

0 commit comments

Comments
 (0)