def _get_action_parameters_from_args(self, action, runner, args):
        """
        Build a dictionary with parameters which will be passed to the action by
        parsing parameters passed to the CLI.

        :param args: CLI argument.
        :type args: ``object``

        :rtype: ``dict``
        """
        action_ref_or_id = action.ref

        def read_file(file_path):
            """
            Read file content and return content as string / unicode.

            NOTE: It's only mean to be used to read non-binary files since API right now doesn't
            support passing binary data.
            """
            if not os.path.exists(file_path):
                raise ValueError('File "%s" doesn\'t exist' % (file_path))

            if not os.path.isfile(file_path):
                raise ValueError('"%s" is not a file' % (file_path))

            with open(file_path, "rb") as fp:
                content = fp.read()

            return content.decode("utf-8")

        def transform_object(value):
            # Also support simple key1=val1,key2=val2 syntax
            if value.startswith("{"):
                # Assume it's JSON
                result = value = json.loads(value)
            else:
                pairs = value.split(",")

                result = {}
                for pair in pairs:
                    split = pair.split("=", 1)

                    if len(split) != 2:
                        continue

                    key, value = split
                    result[key] = value
            return result

        def transform_array(value, action_params=None, auto_dict=False):
            action_params = action_params or {}

            # Sometimes an array parameter only has a single element:
            #
            #     i.e. "st2 run foopack.fooaction arrayparam=51"
            #
            # Normally, json.loads would throw an exception, and the split method
            # would be used. However, since this is an int, not only would
            # splitting not work, but json.loads actually treats this as valid JSON,
            # but as an int, not an array. This causes a mismatch when the API is called.
            #
            # We want to try to handle this first, so it doesn't get accidentally
            # sent to the API as an int, instead of an array of single-element int.
            try:
                # Force this to be a list containing the single int, then
                # cast the whole thing to string so json.loads can handle it
                value = str([int(value)])
            except ValueError:
                # Original value wasn't an int, so just let it continue
                pass

            # At this point, the input is either a a "json.loads"-able construct
            # like [1, 2, 3], or even [1], or it is a comma-separated list,
            # Try both, in that order.
            try:
                result = json.loads(value)
            except ValueError:
                result = [v.strip() for v in value.split(",")]

            # When each values in this array represent dict type, this converts
            # the 'result' to the dict type value.
            if all([isinstance(x, str) and ":" in x for x in result]) and auto_dict:
                result_dict = {}
                for (k, v) in [x.split(":") for x in result]:
                    # To parse values using the 'transformer' according to the type which is
                    # specified in the action metadata, calling 'normalize' method recursively.
                    if (
                        "properties" in action_params
                        and k in action_params["properties"]
                    ):
                        result_dict[k] = normalize(
                            k, v, action_params["properties"], auto_dict=auto_dict
                        )
                    else:
                        result_dict[k] = v
                return [result_dict]

            return result

        transformer = {
            "array": transform_array,
            "boolean": (lambda x: ast.literal_eval(x.capitalize())),
            "integer": int,
            "number": float,
            "object": transform_object,
            "string": str,
        }

        def get_param_type(key, action_params=None):
            action_params = action_params or action.parameters

            param = None
            if key in runner.runner_parameters:
                param = runner.runner_parameters[key]
            elif key in action_params:
                param = action_params[key]

            if param:
                return param["type"]

            return None

        def normalize(name, value, action_params=None, auto_dict=False):
            """The desired type is contained in the action meta-data, so we can look that up
            and call the desired "caster" function listed in the "transformer" dict
            """
            action_params = action_params or action.parameters

            # By default, this method uses a parameter which is defined in the action metadata.
            # This method assume to be called recursively for parsing values in an array of objects
            # type value according to the nested action metadata definition.
            #
            # This is a best practice to pass a list value as default argument to prevent
            # unforeseen consequence by being created a persistent object.

            # Users can also specify type for each array parameter inside an action metadata
            # (items: type: int for example) and this information is available here so we could
            # also leverage that to cast each array item to the correct type.
            param_type = get_param_type(name, action_params)
            if param_type == "array" and name in action_params:
                return transformer[param_type](
                    value, action_params[name], auto_dict=auto_dict
                )
            elif param_type:
                return transformer[param_type](value)

            return value

        result = {}

        if args.inherit_env:
            result["env"] = self._get_inherited_env_vars()

        if not args.parameters:
            return result

        for idx in range(len(args.parameters)):
            arg = args.parameters[idx]
            if "=" in arg:
                k, v = arg.split("=", 1)

                # Attribute for files are prefixed with "@"
                if k.startswith("@"):
                    k = k[1:]
                    is_file = True
                else:
                    is_file = False

                try:
                    if is_file:
                        # Files are handled a bit differently since we ship the content
                        # over the wire
                        file_path = os.path.normpath(pjoin(os.getcwd(), v))
                        file_name = os.path.basename(file_path)
                        content = read_file(file_path=file_path)

                        if action_ref_or_id == "core.http":
                            # Special case for http runner
                            result["_file_name"] = file_name
                            result["file_content"] = content
                        else:
                            result[k] = content
                    else:
                        # This permits multiple declarations of argument only in the array type.
                        if get_param_type(k) == "array" and k in result:
                            result[k] += normalize(k, v, auto_dict=args.auto_dict)
                        else:
                            result[k] = normalize(k, v, auto_dict=args.auto_dict)

                except Exception as e:
                    # TODO: Move transformers in a separate module and handle
                    # exceptions there
                    if "malformed string" in six.text_type(e):
                        message = (
                            "Invalid value for boolean parameter. "
                            "Valid values are: true, false"
                        )
                        raise ValueError(message)
                    else:
                        raise e
            else:
                result["cmd"] = " ".join(args.parameters[idx:])
                break

        # Special case for http runner
        if "file_content" in result:
            if "method" not in result:
                # Default to POST if a method is not provided
                result["method"] = "POST"

            if "file_name" not in result:
                # File name not provided, use default file name
                result["file_name"] = result["_file_name"]

            del result["_file_name"]

        return result

        Build a dictionary with parameters which will be passed to the action by
        parsing parameters passed to the CLI.

        :param args: CLI argument.
        :type args: ``object``

        :rtype: ``dict``
        
st2client/st2client/commands/action.py