예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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