def get_key_value(self, json_path: str) -> Any: """ Retrieve specific key:value pair from JSON. This function fetches key:value pair from JSON object according to provided path and returns only the value. Parameters: `json_path : str` specifies JSON property path (e.g. field1.field2.[...].fieldn). This function raises an `IncorrectFunctionParameterTypeError` if `json_path` parameter has an incorrect type. This function raises a `JSONPathError` if JSON path is not valid. This function raises any additional exceptions if occurred. Examples: Retrieving key:value pair from a simple object: >>> from robust_json.file import JsonFileParser >>> op = JsonFileParser('test_file.json') # Object from `test_file.json` >> { "test_key": "test_value", "test_arr": [1, 2, 3] } >>> val = op.get_key_value('test_key') >>> val # val = 'test_value' Retrieving element from an array >>> from robust_json.file import JsonFileParser >>> op = JsonFileParser('test_file.json') # Object from `test_file.json` >> { "test_key": "test_value", "test_arr": [1, 2, 3] } >>> arr_val = op.get_key_value('test_arr.[1]') # Path is equal to 'test_arr[1]' >>> arr_val # arr_val = 2 For more information about this method please visit: https://github.com/NickolaiBeloguzov/robust-json/blob/master/README.md#file-module-methods-and-properties """ # Checkint parameter type if type(json_path) != str: raise IncorrectFunctionParameterTypeError("json_path", "str", type(json_path).__name__) json_content = self.active_json # Verifying JSON path if not self.__service.check_json_path(json_path, json_content): raise JSONPathError(f"Path `{json_path}` is not valid.") js_expr = jsonpath.parse(json_path) # Parsing JSON res = [item.value for item in js_expr.find(json_content) ] # Filling an array with all matches if len(res) == 1: return res[0] return res
def check_json_path(self, path: str, json: dict) -> bool: """ Check if JSON path exists This function will check if given JSON path exists in specified JSON object. Parameters: `path : str` specifies property path that needs to be checked. `json : dict` specifies Python dictionary (JSON object), where this JSON path needs to be present. This function returns `True` if path is found and `False` if path cannot be accessed (does not exist). This function raises a `IncorrectFunctionParameterTypeError` exception if one or more of its parameters have incorrect types. This function raises a `JSONPathError` exception is JSON path is equal to an empty string. """ # Checking types of functions' parameters if type(path) != str: raise IncorrectFunctionParameterTypeError( "path", "str", type(path).__name__ ) if path == "": raise JSONPathError("JSON path is empty.") if type(json) != dict: raise IncorrectFunctionParameterTypeError( "json", "dict", type(json).__name__ ) js_expr = jsonpath.parse(path) # Parsing JSON using JSON path # If path is valid, return True. Otherwise return False if js_expr.find(json): return True else: return False
def delete(self, json_path: str, key_or_index: Union[str, int]) -> dict: """ Delete value from JSON. Parameters: `json_path : str` specifies property path, while `key_or_index : Union[str, int]` specifies property name in JSON object or item index in JSON array. For example, if you need to delete value with key `delete_key` located under `field1.field2.field3.delete_key` then parameter `key_or_index` will be equal to 'delete_key' and `json_path` parameter will be equal to `field1.field2.field3`. To delete an item in the array, simply pass this items' index (integer) as `key_or_index` parameter. Note: if you use an array index while `json_path` parameter is pointing to a JSON object or if you use a property name while `json_path` is pointing to a JSON array, this function will raise an exception. This function returns a Python dictionary with updated content. This function raises an `IncorrectFunctionParameterTypeError` exception if one or more of its parameters have incorrect types. This function raises a `JSONPathError` if JSON path is not valid. This function raises any additional exceptions if occurred. Examples: Deleteing a key:value pair from root of simple object: >>> from robust_json.object import JsonObjectParser >>> obj = { "application_name": "PetHome", "version": "1.0", "display": "standalone" } >>> op = JsonObjectParser(obj) >>> op.delete('$', 'display') >>> op.active_json # Output: { "application_name": "PetHome", "version": "1.0" } Deleting item an from array >>> from robust_json.object import JsonObjectParser >>> obj = { "colors": [ "red", "magenta", "green" ] } >>> op = JsonObjectParser(obj) >>> op.delete('colors', 2) >>> op.active_json # Output: { "colors": [ "red", "magenta" ] } Note: if you don't know the item index, you can use `get_item_index` function from `robust_json.ext` package to get it. See the code below: >>> from robust_json.object import JSonObjectParser >>> import robust_json.ext as ext >>> obj = { "colors": [ "red", "magenta", "green" ] } # We'll use the same object >>> op = JsonObjectParser(obj) >>> array = op.get_key_value('colors') # Note: please refer to this function's docs if you have # any questions # array = [ "red", "magenta", "green" ] >>> index = ext.get_item_index('red', array, False) # Note: please refer to this function's docs if you have # any questions # index = 0 >>> op.delete('colors', index) >>> op.active_json # Output: { "colors": [ "magenta", "green" ] } For more information about this method, please visit: https://github.com/NickolaiBeloguzov/robust-json/blob/master/README.md#object-module-methods-and-properties """ # TODO Add link to an appropriate README section from GitHub if type(json_path) != str: raise IncorrectFunctionParameterTypeError("json_path", "str", type(json_path).__name__) if type(key_or_index) not in [str, int]: raise IncorrectFunctionParameterTypeError( "key_or_index", "str or int", type(key_or_index).__name__) json_content = self.active_json if not self.__service.check_json_path(json_path, json_content): raise JSONPathError(f"Path `{json_path}` is not valid.") js_expr = jsonpath.parse(json_path) for item in js_expr.find(json_content): temp = item.value if type(temp) == list: if type(key_or_index) != int: raise TypeError( f"Path `{json_path}` is pointing to a JSON array, therefore `key_or_index` parameter must have an `int` type; got `{type(key_or_index).__name__}` instead." ) del temp[key_or_index] self.active_json = json_content return json_content else: if type(key_or_index) != str: raise TypeError( f"Path `{json_path}` is pointing to a JSON object, therefore `key_or_index` parameter must have a `str` type; got `{type(key_or_index).__name__}` instead." ) del temp[key_or_index] self.active_json = json_content if self.__is_autosaving: if os.path.exists(self.__autosave_path): create_file = False else: create_file = True self.save_to_file(self.__autosave_path, create_file=create_file) return json_content
def update_value( self, json_path: str, key_or_index: Union[str, int], new_value: any, strict_mode: bool = False, ) -> dict: """ Update value in JSON. Parameters: `json_path : str` specifies property path, while `key_or_index : Union[str, int]` specifies key in JSON object/item index in JSON array. For example, if you need to update value with key `update_key` located under `field1.field2.field3.update_key` then parameter `key_or_index` will be equal to 'update_key' and `json_path` parameter will be equal to `field1.field2.field3`. If you want to update value in the root of the object then `json_path` parameter needs to be equal to `$`. To update item in an array, simply pass this items' index (integer) as `key_or_index` parameter. Note: if you use an array index while `json_path` parameter is pointing to the JSON object or if you use a key name while `json_path` is pointing to the JSON array, this function will raise an exception. `new_value : Any` specifies value that will overwrite the old one. `strict_mode : bool` parameter enables Strict Mode. If set to `True`, this function will compare the types of previous value and the new one. If they are not identical, this function will raise an exception. This function returns a Python dictionary with updated content. This function raises an `IncorrectFunctionParameterTypeError` exception if one or more of its' parameters have incorrect types. Tis function raises a `JSONPathError` if JSON path is not valid. This function raises a `JSONStrictModeError` if types of old and new values are not the same while Strict Mode is enabled. This function raises any additional exceptions if occurred. Examples: Updating key:value pair in a root of the object: >>> from robust_json.object import JsonObjectParser >>> obj = { "app_name": "Test App", "version": "1.0.5" } >>> op = JsonObjectParser(obj) >>> op.update('$', 'version', '1.1.0') >>> op.active_json # Output: { "app_name": "Test App", "version": "1.1.0" } Updating item in an array: >>> from robust_json.object import JsonObjectParser >>> obj = { "colors": [ "red", "yellow", "green", "purple" ] } >>> op = JsonObjectParser(obj) >>> op.update_value('colors', 3, "magenta") >>> op.active_json # Output: { "colors": [ "red", "yellow", "green", "magenta" ] } Note: if you don't know an item's index, you can use `get_item_index` function from `robust_json.ext` package to get it. >>> from robust_json.object import JsonObjectParser >>> import robust_json.ext as ext >>> obj = { "colors": [ "red", "yellow", "green", "purple" ] } >>> op = JsonObjectParser(obj) >>> colors_array = op.get_key_value('colors') # Note: please refer to this function's docs if you have # any questions # colors_array = [ "red", "yellow", "green", "purple" ] >>> index = ext.get_item_index('green', colors_array, False) # Note: please refer to this function's docs if you have # any questions # index = 2 >>> op.update_value('colors', index, 'cyan') >>> op.active_json # Output: { "colors": [ "red", "yellow", "cyan", "purple" ] } Updating value with Strict Mode enabled: >>> from robust_json.object import JsonObjectParser >>> obj = { "id": 1046, "name": "Jamie Kellen" } >>> op = JsonObjectParser(obj) >>> op.update_value('$', 'id', 'string', True) # JSONStrictModeError exception is raised. # When Strict Mode is enabled, new value must be the same # type as the previous one (in this case: int) >>> op.update_value('$', 'id', 1087, True) >>> op.active_json # Output: { "id": 1087, "name": "Jamie Kellen" } For more information about this method, please visit: https://github.com/NickolaiBeloguzov/robust-json/blob/master/README.md#object-module-methods-and-properties """ # TODO Add link to an appropriate README section from GitHub if type(json_path) != str: raise IncorrectFunctionParameterTypeError("json_path", "str", type(json_path).__name__) if type(strict_mode) != bool: raise IncorrectFunctionParameterTypeError( "strict_mode", "bool", type(strict_mode).__name__) if type(key_or_index) not in [str, int]: raise IncorrectFunctionParameterTypeError( "key_or_index", "str or int", type(key_or_index).__name__) json_content = self.active_json if not self.__service.check_json_path(json_path, json_content): raise JSONPathError(f"Path `{json_path}` is not valid.") js_expr = jsonpath.parse(json_path) for item in js_expr.find(json_content): temp = item.value if type(temp) == list: if type(key_or_index) != int: raise TypeError( f"Path `{json_path}` is pointing to a JSON array, therefore `key_or_index` parameter must have an `int` type; got `{type(key_or_index).__name__}` instead." ) if strict_mode == True: if type(temp[key_or_index]) != type(new_value): raise JSONStrictModeError( f"If strict mode is enabled, the type of the new value must be identical to the type of the old one ({type(temp[key_or_index]).__name__}); got `{type(new_value).__name__}` instead." ) temp[key_or_index] = new_value self.active_json = json_content return json_content else: if type(key_or_index) != str: raise TypeError( f"Path `{json_path}` is pointing to a JSON object, therefore `key_or_index` parameter must have a `str` type; got `{type(key_or_index).__name__}` instead." ) if strict_mode == True: if type(temp[key_or_index]) != type(new_value): raise JSONStrictModeError( f"If strict mode is enabled, the type of the new value must be identical to the type of the old one ({type(temp[key_or_index]).__name__}); got `{type(new_value).__name__}` instead." ) temp.update({key_or_index: new_value}) self.active_json = json_content if self.__is_autosaving: if os.path.exists(self.__autosave_path): create_file = False else: create_file = True self.save_to_file(self.__autosave_path, create_file=create_file) return json_content
def append(self, json_path: str, append_value: any, append_at_end: bool = False) -> dict: """ Append new value to an existing JSON object. This function takes value and adds it to the JSON object. Parameters: `json_path : str` specifies JSON property path where the given value needs to be added. If there is a need to append a value to the root of the object, this parameter needs to be equal to `$`. `append_value : Any` specifies the value that will be appended to the object. `append_at_end : bool` controls the behaviour of this function regarding JSON arrays of objects (structures like this: [{}, {}, {}, ...]) and general arrays (structures like this: [a, b, c, ...]). It has no influence on other structures. If set to False, function will try to add value to each object of an array. If set to True, function will try to append value at the end of an array. (see examples below). This function returns a Python dictionary with updated content. This function raises a `FunctionParameterTypeError` exception if one or more of its parameters have an incorrect type. This function raises a `ValueError` exception if `append_value` parameter is empty (i.e is equal to an empty string, an empty array or empty dictionary). This function raises a `JSONPathError` exception if JSON path is not valid. This function raises any additional exceptions if occurred. Examples: Adding a simple key:value pair to the root object: >>> from robust_json.object import JsonObjectParser >>> obj = { "key": "value" } >>> op = JsonObjectParser(obj) # You can also pass dictionary directly >>> op.append('$', { 'test': 'test' }) >>> op.active_json # Output: { "key": "value", "test": "test" } Adding a new JSON object to an array of objects: >>> from robust_json.object import JsonObjectParser >>> op = JsonObjectParser({ "users": [ {"id": 1, "name": "Ken"}, { "id": 2, "name": "Liza" } ] }) >>> op.append('users', { 'id': 3, 'name': 'Nick' }, True) >>> op.active_json # Output: { "users": [ {"id": 1, "name": "Ken"}, { "id": 2, "name": "Liza" }, { "id": 3, "name": "Nick" } ] } Adding a key:value pair in each object of an array of objects >>> from robust_json.object import JsonObjectParser >>> obj = { "users": [ {"id": 1, "name": "Ken"}, { "id": 2, "name": "Liza" } ] } >>> op = JsonObjectParser(obj) # You can also pass dictionary directly >>> op.append('users', { 'role': 'guest' }) >>> op.active_json # Output: { "users": [ { "id": 1, "name": "Ken", "role": "guest" }, { "id": 2, "name": "Liza", "role": "guest" } ] } Adding a new element to an array of strings: >>> from robust_json.object import JsonObjectParser >>> obj = { "colors": [ "red", "blue" ] } >>> op = JsonObjectParser(obj) >>> op.append('colors', 'green') >>> op.active_json # Output: { "colors": [ "red", "blue" ] } # Nothing has appended. It's because this function tried to append given # value to each string in array and failed # To fix this, you need to set `append_at_end` parameter to `True` >>> op.append('colors', 'green', True) >>> op.active_json # Output: { "colors": [ "red", "blue", "green" ] } For more information about this method, please visit: https://github.com/NickolaiBeloguzov/robust-json/blob/master/README.md#object-module-methods-and-properties """ if type(json_path) != str: raise IncorrectFunctionParameterTypeError("json_path", "str", type(json_path).__name__) if type(append_at_end) != bool: raise IncorrectFunctionParameterTypeError( "append_at_end", "bool", type(append_at_end).__name__) empty_obj = [[], {}, ""] if append_value in empty_obj: raise ValueError(f"Parameter `append_value` is empty.") json_content = self.active_json if not self.__service.check_json_path(json_path, json_content): raise JSONPathError(f"Path `{json_path}` is not valid.") js_expr = jsonpath.parse(json_path) for item in js_expr.find(json_content): temp = item.value if type(temp) == list: if append_at_end == True: temp.append(append_value) self.active_json = json_content return json_content else: for i in iter(temp): if type(i) == dict: if type(append_value) == dict: i.update(append_value) else: raise TypeError( f"To append to a JSON object, parameter `append_value` must be a dictionary; got `{type(append_value).__name__}` instead." ) self.active_json = json_content return json_content temp.update(append_value) self.active_json = json_content if self.__is_autosaving: if os.path.exists(self.__autosave_path): create_file = False else: create_file = True self.save_to_file(self.__autosave_path, create_file=create_file) return json_content